migrate to Protobuf ES

This commit is contained in:
seavor 2026-04-13 15:03:57 -05:00
parent 68e22d22bf
commit fd55f4fb7f
133 changed files with 1745 additions and 1621 deletions

View file

@ -17,7 +17,7 @@ vi.mock('./services/ProtobufService', () => ({
vi.mock('./persistence', () => ({
RoomPersistence: { clearStore: vi.fn() },
SessionPersistence: { clearStore: vi.fn() },
SessionPersistence: { clearStore: vi.fn(), initialized: vi.fn() },
}));
import { WebClient } from './WebClient';
@ -60,6 +60,10 @@ describe('WebClient', () => {
messageSubject.next(event);
expect(client.protobuf.handleMessageEvent).toHaveBeenCalledWith(event);
});
it('calls SessionPersistence.initialized', () => {
expect(SessionPersistence.initialized).toHaveBeenCalled();
});
});
describe('connect', () => {

View file

@ -47,6 +47,8 @@ export class WebClient {
this.protobuf.handleMessageEvent(message);
});
SessionPersistence.initialized();
if (import.meta.env.MODE !== 'test') {
console.log(this);
}

View file

@ -4,8 +4,8 @@
* @param mockFn - The vi.Mock for the BackendService send method
* (e.g. BackendService.sendSessionCommand as vi.Mock).
* @param optsArgIndex - Index of the options argument in the mock call.
* Defaults to 2 (commandName, params, options).
* Use 3 for sendRoomCommand (roomId, commandName, params, options).
* Defaults to 2 (ext, value, options).
* Use 3 for sendRoomCommand (roomId, ext, value, options).
*/
export function makeCallbackHelpers(mockFn: vi.Mock, optsArgIndex = 2) {
function getLastSendOpts() {

View file

@ -1,54 +1,9 @@
/**
* Shared mock factories for websocket layer unit tests.
* Import the helpers you need in each spec file via:
* import { makeMockProtoRoot, makeMockWebSocket } from '../__mocks__/helpers';
* import { makeMockWebSocket } from '../__mocks__/helpers';
*/
/** Builds a minimal mock of ProtoController.root */
export function makeMockProtoRoot() {
const encode = { finish: vi.fn().mockReturnValue(new Uint8Array()) };
return {
CommandContainer: {
create: vi.fn(args => ({ ...args })),
encode: vi.fn().mockReturnValue(encode),
},
SessionCommand: { create: vi.fn(args => ({ ...args })) },
RoomCommand: { create: vi.fn(args => ({ ...args })) },
ModeratorCommand: { create: vi.fn(args => ({ ...args })) },
AdminCommand: { create: vi.fn(args => ({ ...args })) },
ServerMessage: {
decode: vi.fn(),
MessageType: {
RESPONSE: 'RESPONSE',
ROOM_EVENT: 'ROOM_EVENT',
SESSION_EVENT: 'SESSION_EVENT',
GAME_EVENT_CONTAINER: 'GAME_EVENT_CONTAINER',
},
},
Response: {
ResponseCode: {
RespOk: 0,
RespRegistrationRequired: 1,
},
},
Event_ServerIdentification: {
ServerOptions: { SupportsPasswordHash: 2 },
},
Event_ConnectionClosed: {
CloseReason: {
USER_LIMIT_REACHED: 1,
TOO_MANY_CONNECTIONS: 2,
BANNED: 3,
DEMOTED: 4,
SERVER_SHUTDOWN: 5,
USERNAMEINVALID: 6,
LOGGEDINELSEWERE: 7,
OTHER: 8,
},
},
};
}
/** Builds a mock WebSocket instance */
export function makeMockWebSocketInstance() {
return {

View file

@ -25,38 +25,6 @@ export function makeWebClientMock() {
};
}
/** Superset ProtoController.root mock — includes all ResponseCode values and Event_ServerIdentification. */
export function makeProtoControllerRootMock() {
return {
Response: {
ResponseCode: {
RespOk: 0,
RespClientUpdateRequired: 1,
RespWrongPassword: 2,
RespUsernameInvalid: 3,
RespWouldOverwriteOldSession: 4,
RespUserIsBanned: 5,
RespRegistrationRequired: 6,
RespClientIdRequired: 7,
RespContextError: 8,
RespAccountNotActivated: 9,
RespRegistrationAccepted: 10,
RespRegistrationAcceptedNeedsActivation: 11,
RespUserAlreadyExists: 12,
RespPasswordTooShort: 13,
RespEmailRequiredToRegister: 14,
RespEmailBlackListed: 15,
RespTooManyRequests: 16,
RespRegistrationDisabled: 17,
RespActivationAccepted: 18,
},
},
Event_ServerIdentification: {
ServerOptions: { SupportsPasswordHash: 2 },
},
};
}
/** Utils mock with unified return values. */
export function makeUtilsMock() {
return {

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_AdjustMod_ext, Command_AdjustModSchema } from 'generated/proto/admin_commands_pb';
import { AdminPersistence } from '../../persistence';
export function adjustMod(userName: string, shouldBeMod?: boolean, shouldBeJudge?: boolean): void {
BackendService.sendAdminCommand('Command_AdjustMod', { userName, shouldBeMod, shouldBeJudge }, {
BackendService.sendAdminCommand(Command_AdjustMod_ext, create(Command_AdjustModSchema, { userName, shouldBeMod, shouldBeJudge }), {
onSuccess: () => {
AdminPersistence.adjustMod(userName, shouldBeMod, shouldBeJudge);
},

View file

@ -22,7 +22,8 @@ import { shutdownServer } from './shutdownServer';
import { updateServerMessage } from './updateServerMessage';
const { getLastSendOpts, invokeOnSuccess } = makeCallbackHelpers(
BackendService.sendAdminCommand as vi.Mock
BackendService.sendAdminCommand as vi.Mock,
2
);
beforeEach(() => vi.clearAllMocks());
@ -34,11 +35,7 @@ describe('adjustMod', () => {
it('calls sendAdminCommand with Command_AdjustMod', () => {
adjustMod('alice', true, false);
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith(
'Command_AdjustMod',
expect.objectContaining({ userName: 'alice', shouldBeMod: true, shouldBeJudge: false }),
expect.any(Object)
);
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.adjustMod', () => {
@ -55,7 +52,7 @@ describe('reloadConfig', () => {
it('calls sendAdminCommand with Command_ReloadConfig', () => {
reloadConfig();
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith('Command_ReloadConfig', {}, expect.any(Object));
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.reloadConfig', () => {
@ -72,11 +69,7 @@ describe('shutdownServer', () => {
it('calls sendAdminCommand with Command_ShutdownServer', () => {
shutdownServer('maintenance', 10);
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith(
'Command_ShutdownServer',
{ reason: 'maintenance', minutes: 10 },
expect.any(Object)
);
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.shutdownServer', () => {
@ -93,7 +86,7 @@ describe('updateServerMessage', () => {
it('calls sendAdminCommand with Command_UpdateServerMessage', () => {
updateServerMessage();
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith('Command_UpdateServerMessage', {}, expect.any(Object));
expect(BackendService.sendAdminCommand).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), expect.any(Object));
});
it('onSuccess calls AdminPersistence.updateServerMessage', () => {

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReloadConfig_ext, Command_ReloadConfigSchema } from 'generated/proto/admin_commands_pb';
import { AdminPersistence } from '../../persistence';
export function reloadConfig(): void {
BackendService.sendAdminCommand('Command_ReloadConfig', {}, {
BackendService.sendAdminCommand(Command_ReloadConfig_ext, create(Command_ReloadConfigSchema), {
onSuccess: () => {
AdminPersistence.reloadConfig();
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ShutdownServer_ext, Command_ShutdownServerSchema } from 'generated/proto/admin_commands_pb';
import { AdminPersistence } from '../../persistence';
export function shutdownServer(reason: string, minutes: number): void {
BackendService.sendAdminCommand('Command_ShutdownServer', { reason, minutes }, {
BackendService.sendAdminCommand(Command_ShutdownServer_ext, create(Command_ShutdownServerSchema, { reason, minutes }), {
onSuccess: () => {
AdminPersistence.shutdownServer();
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_UpdateServerMessage_ext, Command_UpdateServerMessageSchema } from 'generated/proto/admin_commands_pb';
import { AdminPersistence } from '../../persistence';
export function updateServerMessage(): void {
BackendService.sendAdminCommand('Command_UpdateServerMessage', {}, {
BackendService.sendAdminCommand(Command_UpdateServerMessage_ext, create(Command_UpdateServerMessageSchema), {
onSuccess: () => {
AdminPersistence.updateServerMessage();
},

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_AttachCardSchema, Command_AttachCard_ext } from 'generated/proto/command_attach_card_pb';
import { AttachCardParams } from 'types';
export function attachCard(gameId: number, params: AttachCardParams): void {
BackendService.sendGameCommand(gameId, 'Command_AttachCard', params);
BackendService.sendGameCommand(gameId, Command_AttachCard_ext, create(Command_AttachCardSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ChangeZonePropertiesSchema, Command_ChangeZoneProperties_ext } from 'generated/proto/command_change_zone_properties_pb';
import { ChangeZonePropertiesParams } from 'types';
export function changeZoneProperties(gameId: number, params: ChangeZonePropertiesParams): void {
BackendService.sendGameCommand(gameId, 'Command_ChangeZoneProperties', params);
BackendService.sendGameCommand(gameId, Command_ChangeZoneProperties_ext, create(Command_ChangeZonePropertiesSchema, params));
}

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ConcedeSchema, Command_Concede_ext } from 'generated/proto/command_concede_pb';
export function concede(gameId: number): void {
BackendService.sendGameCommand(gameId, 'Command_Concede', {});
BackendService.sendGameCommand(gameId, Command_Concede_ext, create(Command_ConcedeSchema));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_CreateArrowSchema, Command_CreateArrow_ext } from 'generated/proto/command_create_arrow_pb';
import { CreateArrowParams } from 'types';
export function createArrow(gameId: number, params: CreateArrowParams): void {
BackendService.sendGameCommand(gameId, 'Command_CreateArrow', params);
BackendService.sendGameCommand(gameId, Command_CreateArrow_ext, create(Command_CreateArrowSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_CreateCounterSchema, Command_CreateCounter_ext } from 'generated/proto/command_create_counter_pb';
import { CreateCounterParams } from 'types';
export function createCounter(gameId: number, params: CreateCounterParams): void {
BackendService.sendGameCommand(gameId, 'Command_CreateCounter', params);
BackendService.sendGameCommand(gameId, Command_CreateCounter_ext, create(Command_CreateCounterSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_CreateTokenSchema, Command_CreateToken_ext } from 'generated/proto/command_create_token_pb';
import { CreateTokenParams } from 'types';
export function createToken(gameId: number, params: CreateTokenParams): void {
BackendService.sendGameCommand(gameId, 'Command_CreateToken', params);
BackendService.sendGameCommand(gameId, Command_CreateToken_ext, create(Command_CreateTokenSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeckSelectSchema, Command_DeckSelect_ext } from 'generated/proto/command_deck_select_pb';
import { DeckSelectParams } from 'types';
export function deckSelect(gameId: number, params: DeckSelectParams): void {
BackendService.sendGameCommand(gameId, 'Command_DeckSelect', params);
BackendService.sendGameCommand(gameId, Command_DeckSelect_ext, create(Command_DeckSelectSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DelCounterSchema, Command_DelCounter_ext } from 'generated/proto/command_del_counter_pb';
import { DelCounterParams } from 'types';
export function delCounter(gameId: number, params: DelCounterParams): void {
BackendService.sendGameCommand(gameId, 'Command_DelCounter', params);
BackendService.sendGameCommand(gameId, Command_DelCounter_ext, create(Command_DelCounterSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeleteArrowSchema, Command_DeleteArrow_ext } from 'generated/proto/command_delete_arrow_pb';
import { DeleteArrowParams } from 'types';
export function deleteArrow(gameId: number, params: DeleteArrowParams): void {
BackendService.sendGameCommand(gameId, 'Command_DeleteArrow', params);
BackendService.sendGameCommand(gameId, Command_DeleteArrow_ext, create(Command_DeleteArrowSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DrawCardsSchema, Command_DrawCards_ext } from 'generated/proto/command_draw_cards_pb';
import { DrawCardsParams } from 'types';
export function drawCards(gameId: number, params: DrawCardsParams): void {
BackendService.sendGameCommand(gameId, 'Command_DrawCards', params);
BackendService.sendGameCommand(gameId, Command_DrawCards_ext, create(Command_DrawCardsSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DumpZoneSchema, Command_DumpZone_ext } from 'generated/proto/command_dump_zone_pb';
import { DumpZoneParams } from 'types';
export function dumpZone(gameId: number, params: DumpZoneParams): void {
BackendService.sendGameCommand(gameId, 'Command_DumpZone', params);
BackendService.sendGameCommand(gameId, Command_DumpZone_ext, create(Command_DumpZoneSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_FlipCardSchema, Command_FlipCard_ext } from 'generated/proto/command_flip_card_pb';
import { FlipCardParams } from 'types';
export function flipCard(gameId: number, params: FlipCardParams): void {
BackendService.sendGameCommand(gameId, 'Command_FlipCard', params);
BackendService.sendGameCommand(gameId, Command_FlipCard_ext, create(Command_FlipCardSchema, params));
}

View file

@ -1,4 +1,37 @@
import { BackendService } from '../../services/BackendService';
import { create, setExtension } from '@bufbuild/protobuf';
import { GameCommandSchema, Command_Judge_ext } from 'generated/proto/game_commands_pb';
import { Command_DrawCardsSchema, Command_DrawCards_ext } from 'generated/proto/command_draw_cards_pb';
import { Command_AttachCard_ext } from 'generated/proto/command_attach_card_pb';
import { Command_ChangeZoneProperties_ext } from 'generated/proto/command_change_zone_properties_pb';
import { Command_Concede_ext, Command_Unconcede_ext } from 'generated/proto/command_concede_pb';
import { Command_CreateArrow_ext } from 'generated/proto/command_create_arrow_pb';
import { Command_CreateCounter_ext } from 'generated/proto/command_create_counter_pb';
import { Command_CreateToken_ext } from 'generated/proto/command_create_token_pb';
import { Command_DeckSelect_ext } from 'generated/proto/command_deck_select_pb';
import { Command_DelCounter_ext } from 'generated/proto/command_del_counter_pb';
import { Command_DeleteArrow_ext } from 'generated/proto/command_delete_arrow_pb';
import { Command_DumpZone_ext } from 'generated/proto/command_dump_zone_pb';
import { Command_FlipCard_ext } from 'generated/proto/command_flip_card_pb';
import { Command_GameSay_ext } from 'generated/proto/command_game_say_pb';
import { Command_IncCardCounter_ext } from 'generated/proto/command_inc_card_counter_pb';
import { Command_IncCounter_ext } from 'generated/proto/command_inc_counter_pb';
import { Command_KickFromGame_ext } from 'generated/proto/command_kick_from_game_pb';
import { Command_LeaveGame_ext } from 'generated/proto/command_leave_game_pb';
import { Command_MoveCard_ext } from 'generated/proto/command_move_card_pb';
import { Command_Mulligan_ext } from 'generated/proto/command_mulligan_pb';
import { Command_NextTurn_ext } from 'generated/proto/command_next_turn_pb';
import { Command_ReadyStart_ext } from 'generated/proto/command_ready_start_pb';
import { Command_RevealCards_ext } from 'generated/proto/command_reveal_cards_pb';
import { Command_ReverseTurn_ext } from 'generated/proto/command_reverse_turn_pb';
import { Command_SetActivePhase_ext } from 'generated/proto/command_set_active_phase_pb';
import { Command_SetCardAttr_ext } from 'generated/proto/command_set_card_attr_pb';
import { Command_SetCardCounter_ext } from 'generated/proto/command_set_card_counter_pb';
import { Command_SetCounter_ext } from 'generated/proto/command_set_counter_pb';
import { Command_SetSideboardLock_ext } from 'generated/proto/command_set_sideboard_lock_pb';
import { Command_SetSideboardPlan_ext } from 'generated/proto/command_set_sideboard_plan_pb';
import { Command_Shuffle_ext } from 'generated/proto/command_shuffle_pb';
import { Command_UndoDraw_ext } from 'generated/proto/command_undo_draw_pb';
import { attachCard } from './attachCard';
import { changeZoneProperties } from './changeZoneProperties';
import { concede } from './concede';
@ -38,7 +71,6 @@ vi.mock('../../services/BackendService', () => ({
}));
const gameId = 1;
const params = {} as any;
beforeEach(() => {
(BackendService.sendGameCommand as vi.Mock).mockClear();
@ -46,172 +78,208 @@ beforeEach(() => {
describe('Game commands — delegate to BackendService.sendGameCommand', () => {
it('attachCard sends Command_AttachCard', () => {
attachCard(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_AttachCard', params);
attachCard(gameId, { cardId: 10, startZone: 'hand' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_AttachCard_ext, expect.objectContaining({ cardId: 10, startZone: 'hand' })
);
});
it('changeZoneProperties sends Command_ChangeZoneProperties', () => {
changeZoneProperties(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_ChangeZoneProperties', params);
changeZoneProperties(gameId, { zoneName: 'side' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_ChangeZoneProperties_ext, expect.objectContaining({ zoneName: 'side' })
);
});
it('concede sends Command_Concede with empty object', () => {
concede(gameId);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_Concede', {});
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Concede_ext, expect.any(Object));
});
it('createArrow sends Command_CreateArrow', () => {
createArrow(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_CreateArrow', params);
createArrow(gameId, { startPlayerId: 1, startZone: 'hand' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_CreateArrow_ext, expect.objectContaining({ startPlayerId: 1, startZone: 'hand' })
);
});
it('createCounter sends Command_CreateCounter', () => {
createCounter(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_CreateCounter', params);
createCounter(gameId, { counterName: 'life' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_CreateCounter_ext, expect.objectContaining({ counterName: 'life' })
);
});
it('createToken sends Command_CreateToken', () => {
createToken(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_CreateToken', params);
createToken(gameId, { cardName: 'Goblin', zone: 'play' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_CreateToken_ext, expect.objectContaining({ cardName: 'Goblin', zone: 'play' })
);
});
it('deckSelect sends Command_DeckSelect', () => {
deckSelect(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_DeckSelect', params);
deckSelect(gameId, { deckId: 5 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DeckSelect_ext, expect.objectContaining({ deckId: 5 }));
});
it('delCounter sends Command_DelCounter', () => {
delCounter(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_DelCounter', params);
delCounter(gameId, { counterId: 3 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DelCounter_ext, expect.objectContaining({ counterId: 3 }));
});
it('deleteArrow sends Command_DeleteArrow', () => {
deleteArrow(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_DeleteArrow', params);
deleteArrow(gameId, { arrowId: 2 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DeleteArrow_ext, expect.objectContaining({ arrowId: 2 }));
});
it('drawCards sends Command_DrawCards', () => {
drawCards(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_DrawCards', params);
drawCards(gameId, { number: 3 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DrawCards_ext, expect.objectContaining({ number: 3 }));
});
it('dumpZone sends Command_DumpZone', () => {
dumpZone(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_DumpZone', params);
dumpZone(gameId, { playerId: 2, zoneName: 'library' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_DumpZone_ext, expect.objectContaining({ playerId: 2, zoneName: 'library' })
);
});
it('flipCard sends Command_FlipCard', () => {
flipCard(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_FlipCard', params);
flipCard(gameId, { cardId: 7, faceDown: false });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_FlipCard_ext, expect.objectContaining({ cardId: 7, faceDown: false })
);
});
it('gameSay sends Command_GameSay', () => {
gameSay(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_GameSay', params);
gameSay(gameId, { message: 'hello' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_GameSay_ext, expect.objectContaining({ message: 'hello' }));
});
it('incCardCounter sends Command_IncCardCounter', () => {
incCardCounter(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_IncCardCounter', params);
incCardCounter(gameId, { cardId: 5, counterId: 1 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_IncCardCounter_ext, expect.objectContaining({ cardId: 5, counterId: 1 })
);
});
it('incCounter sends Command_IncCounter', () => {
incCounter(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_IncCounter', params);
incCounter(gameId, { counterId: 1, delta: 5 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_IncCounter_ext, expect.objectContaining({ counterId: 1, delta: 5 })
);
});
it('kickFromGame sends Command_KickFromGame', () => {
kickFromGame(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_KickFromGame', params);
kickFromGame(gameId, { playerId: 2 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_KickFromGame_ext, expect.objectContaining({ playerId: 2 }));
});
it('leaveGame sends Command_LeaveGame with empty object', () => {
leaveGame(gameId);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_LeaveGame', {});
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_LeaveGame_ext, expect.any(Object));
});
it('moveCard sends Command_MoveCard', () => {
moveCard(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_MoveCard', params);
moveCard(gameId, { startZone: 'hand', targetZone: 'graveyard' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_MoveCard_ext,
expect.objectContaining({ startZone: 'hand', targetZone: 'graveyard' })
);
});
it('mulligan sends Command_Mulligan', () => {
mulligan(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_Mulligan', params);
mulligan(gameId, { number: 7 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Mulligan_ext, expect.objectContaining({ number: 7 }));
});
it('nextTurn sends Command_NextTurn with empty object', () => {
nextTurn(gameId);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_NextTurn', {});
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_NextTurn_ext, expect.any(Object));
});
it('readyStart sends Command_ReadyStart', () => {
readyStart(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_ReadyStart', params);
readyStart(gameId, { ready: true });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_ReadyStart_ext, expect.objectContaining({ ready: true }));
});
it('revealCards sends Command_RevealCards', () => {
revealCards(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_RevealCards', params);
revealCards(gameId, { zoneName: 'hand', cardId: [1, 2] });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_RevealCards_ext, expect.objectContaining({ zoneName: 'hand', cardId: [1, 2] })
);
});
it('reverseTurn sends Command_ReverseTurn with empty object', () => {
reverseTurn(gameId);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_ReverseTurn', {});
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_ReverseTurn_ext, expect.any(Object));
});
it('setActivePhase sends Command_SetActivePhase', () => {
setActivePhase(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_SetActivePhase', params);
setActivePhase(gameId, { phase: 2 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_SetActivePhase_ext, expect.objectContaining({ phase: 2 }));
});
it('setCardAttr sends Command_SetCardAttr', () => {
setCardAttr(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_SetCardAttr', params);
setCardAttr(gameId, { zone: 'play', cardId: 5, attrValue: '2' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetCardAttr_ext,
expect.objectContaining({ zone: 'play', cardId: 5, attrValue: '2' })
);
});
it('setCardCounter sends Command_SetCardCounter', () => {
setCardCounter(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_SetCardCounter', params);
setCardCounter(gameId, { cardId: 5, counterId: 1 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetCardCounter_ext, expect.objectContaining({ cardId: 5, counterId: 1 })
);
});
it('setCounter sends Command_SetCounter', () => {
setCounter(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_SetCounter', params);
setCounter(gameId, { counterId: 1, value: 10 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetCounter_ext, expect.objectContaining({ counterId: 1, value: 10 })
);
});
it('setSideboardLock sends Command_SetSideboardLock', () => {
setSideboardLock(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_SetSideboardLock', params);
setSideboardLock(gameId, { locked: true });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetSideboardLock_ext, expect.objectContaining({ locked: true })
);
});
it('setSideboardPlan sends Command_SetSideboardPlan', () => {
setSideboardPlan(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_SetSideboardPlan', params);
setSideboardPlan(gameId, { moveList: [] });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetSideboardPlan_ext, expect.objectContaining({ moveList: expect.any(Array) })
);
});
it('shuffle sends Command_Shuffle', () => {
shuffle(gameId, params);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_Shuffle', params);
shuffle(gameId, { zoneName: 'hand' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Shuffle_ext, expect.objectContaining({ zoneName: 'hand' }));
});
it('undoDraw sends Command_UndoDraw with empty object', () => {
undoDraw(gameId);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_UndoDraw', {});
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_UndoDraw_ext, expect.any(Object));
});
it('unconcede sends Command_Unconcede with empty object', () => {
unconcede(gameId);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_Unconcede', {});
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Unconcede_ext, expect.any(Object));
});
it('judge sends Command_Judge with targetId and wrapped gameCommand array', () => {
const targetId = 3;
const innerGameCommand = { '.Command_DrawCards.ext': { numberOfCards: 2 } };
judge(gameId, targetId, innerGameCommand);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, 'Command_Judge', {
targetId,
gameCommand: [innerGameCommand],
});
const innerCmd = create(GameCommandSchema);
setExtension(innerCmd, Command_DrawCards_ext, create(Command_DrawCardsSchema, { number: 2 }));
judge(gameId, targetId, innerCmd);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
gameId,
Command_Judge_ext,
expect.objectContaining({ targetId: 3, gameCommand: expect.any(Array) })
);
});
});

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GameSaySchema, Command_GameSay_ext } from 'generated/proto/command_game_say_pb';
import { GameSayParams } from 'types';
export function gameSay(gameId: number, params: GameSayParams): void {
BackendService.sendGameCommand(gameId, 'Command_GameSay', params);
BackendService.sendGameCommand(gameId, Command_GameSay_ext, create(Command_GameSaySchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_IncCardCounterSchema, Command_IncCardCounter_ext } from 'generated/proto/command_inc_card_counter_pb';
import { IncCardCounterParams } from 'types';
export function incCardCounter(gameId: number, params: IncCardCounterParams): void {
BackendService.sendGameCommand(gameId, 'Command_IncCardCounter', params);
BackendService.sendGameCommand(gameId, Command_IncCardCounter_ext, create(Command_IncCardCounterSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_IncCounterSchema, Command_IncCounter_ext } from 'generated/proto/command_inc_counter_pb';
import { IncCounterParams } from 'types';
export function incCounter(gameId: number, params: IncCounterParams): void {
BackendService.sendGameCommand(gameId, 'Command_IncCounter', params);
BackendService.sendGameCommand(gameId, Command_IncCounter_ext, create(Command_IncCounterSchema, params));
}

View file

@ -1,8 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_JudgeSchema, Command_Judge_ext } from 'generated/proto/game_commands_pb';
import type { GameCommand } from 'generated/proto/game_commands_pb';
export function judge(gameId: number, targetId: number, innerGameCommand: any): void {
BackendService.sendGameCommand(gameId, 'Command_Judge', {
export function judge(gameId: number, targetId: number, innerGameCommand: GameCommand): void {
BackendService.sendGameCommand(gameId, Command_Judge_ext, create(Command_JudgeSchema, {
targetId,
gameCommand: [innerGameCommand],
});
}));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_KickFromGameSchema, Command_KickFromGame_ext } from 'generated/proto/command_kick_from_game_pb';
import { KickFromGameParams } from 'types';
export function kickFromGame(gameId: number, params: KickFromGameParams): void {
BackendService.sendGameCommand(gameId, 'Command_KickFromGame', params);
BackendService.sendGameCommand(gameId, Command_KickFromGame_ext, create(Command_KickFromGameSchema, params));
}

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_LeaveGameSchema, Command_LeaveGame_ext } from 'generated/proto/command_leave_game_pb';
export function leaveGame(gameId: number): void {
BackendService.sendGameCommand(gameId, 'Command_LeaveGame', {});
BackendService.sendGameCommand(gameId, Command_LeaveGame_ext, create(Command_LeaveGameSchema));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_MoveCardSchema, Command_MoveCard_ext } from 'generated/proto/command_move_card_pb';
import { MoveCardParams } from 'types';
export function moveCard(gameId: number, params: MoveCardParams): void {
BackendService.sendGameCommand(gameId, 'Command_MoveCard', params);
BackendService.sendGameCommand(gameId, Command_MoveCard_ext, create(Command_MoveCardSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_MulliganSchema, Command_Mulligan_ext } from 'generated/proto/command_mulligan_pb';
import { MulliganParams } from 'types';
export function mulligan(gameId: number, params: MulliganParams): void {
BackendService.sendGameCommand(gameId, 'Command_Mulligan', params);
BackendService.sendGameCommand(gameId, Command_Mulligan_ext, create(Command_MulliganSchema, params));
}

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_NextTurnSchema, Command_NextTurn_ext } from 'generated/proto/command_next_turn_pb';
export function nextTurn(gameId: number): void {
BackendService.sendGameCommand(gameId, 'Command_NextTurn', {});
BackendService.sendGameCommand(gameId, Command_NextTurn_ext, create(Command_NextTurnSchema));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReadyStartSchema, Command_ReadyStart_ext } from 'generated/proto/command_ready_start_pb';
import { ReadyStartParams } from 'types';
export function readyStart(gameId: number, params: ReadyStartParams): void {
BackendService.sendGameCommand(gameId, 'Command_ReadyStart', params);
BackendService.sendGameCommand(gameId, Command_ReadyStart_ext, create(Command_ReadyStartSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_RevealCardsSchema, Command_RevealCards_ext } from 'generated/proto/command_reveal_cards_pb';
import { RevealCardsParams } from 'types';
export function revealCards(gameId: number, params: RevealCardsParams): void {
BackendService.sendGameCommand(gameId, 'Command_RevealCards', params);
BackendService.sendGameCommand(gameId, Command_RevealCards_ext, create(Command_RevealCardsSchema, params));
}

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReverseTurnSchema, Command_ReverseTurn_ext } from 'generated/proto/command_reverse_turn_pb';
export function reverseTurn(gameId: number): void {
BackendService.sendGameCommand(gameId, 'Command_ReverseTurn', {});
BackendService.sendGameCommand(gameId, Command_ReverseTurn_ext, create(Command_ReverseTurnSchema));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_SetActivePhaseSchema, Command_SetActivePhase_ext } from 'generated/proto/command_set_active_phase_pb';
import { SetActivePhaseParams } from 'types';
export function setActivePhase(gameId: number, params: SetActivePhaseParams): void {
BackendService.sendGameCommand(gameId, 'Command_SetActivePhase', params);
BackendService.sendGameCommand(gameId, Command_SetActivePhase_ext, create(Command_SetActivePhaseSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_SetCardAttrSchema, Command_SetCardAttr_ext } from 'generated/proto/command_set_card_attr_pb';
import { SetCardAttrParams } from 'types';
export function setCardAttr(gameId: number, params: SetCardAttrParams): void {
BackendService.sendGameCommand(gameId, 'Command_SetCardAttr', params);
BackendService.sendGameCommand(gameId, Command_SetCardAttr_ext, create(Command_SetCardAttrSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_SetCardCounterSchema, Command_SetCardCounter_ext } from 'generated/proto/command_set_card_counter_pb';
import { SetCardCounterParams } from 'types';
export function setCardCounter(gameId: number, params: SetCardCounterParams): void {
BackendService.sendGameCommand(gameId, 'Command_SetCardCounter', params);
BackendService.sendGameCommand(gameId, Command_SetCardCounter_ext, create(Command_SetCardCounterSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_SetCounterSchema, Command_SetCounter_ext } from 'generated/proto/command_set_counter_pb';
import { SetCounterParams } from 'types';
export function setCounter(gameId: number, params: SetCounterParams): void {
BackendService.sendGameCommand(gameId, 'Command_SetCounter', params);
BackendService.sendGameCommand(gameId, Command_SetCounter_ext, create(Command_SetCounterSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_SetSideboardLockSchema, Command_SetSideboardLock_ext } from 'generated/proto/command_set_sideboard_lock_pb';
import { SetSideboardLockParams } from 'types';
export function setSideboardLock(gameId: number, params: SetSideboardLockParams): void {
BackendService.sendGameCommand(gameId, 'Command_SetSideboardLock', params);
BackendService.sendGameCommand(gameId, Command_SetSideboardLock_ext, create(Command_SetSideboardLockSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_SetSideboardPlanSchema, Command_SetSideboardPlan_ext } from 'generated/proto/command_set_sideboard_plan_pb';
import { SetSideboardPlanParams } from 'types';
export function setSideboardPlan(gameId: number, params: SetSideboardPlanParams): void {
BackendService.sendGameCommand(gameId, 'Command_SetSideboardPlan', params);
BackendService.sendGameCommand(gameId, Command_SetSideboardPlan_ext, create(Command_SetSideboardPlanSchema, params));
}

View file

@ -1,6 +1,8 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ShuffleSchema, Command_Shuffle_ext } from 'generated/proto/command_shuffle_pb';
import { ShuffleParams } from 'types';
export function shuffle(gameId: number, params: ShuffleParams): void {
BackendService.sendGameCommand(gameId, 'Command_Shuffle', params);
BackendService.sendGameCommand(gameId, Command_Shuffle_ext, create(Command_ShuffleSchema, params));
}

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_UnconcedeSchema, Command_Unconcede_ext } from 'generated/proto/command_concede_pb';
export function unconcede(gameId: number): void {
BackendService.sendGameCommand(gameId, 'Command_Unconcede', {});
BackendService.sendGameCommand(gameId, Command_Unconcede_ext, create(Command_UnconcedeSchema));
}

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_UndoDrawSchema, Command_UndoDraw_ext } from 'generated/proto/command_undo_draw_pb';
export function undoDraw(gameId: number): void {
BackendService.sendGameCommand(gameId, 'Command_UndoDraw', {});
BackendService.sendGameCommand(gameId, Command_UndoDraw_ext, create(Command_UndoDrawSchema));
}

View file

@ -1,11 +1,13 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_BanFromServer_ext, Command_BanFromServerSchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
export function banFromServer(minutes: number, userName?: string, address?: string, reason?: string,
visibleReason?: string, clientid?: string, removeMessages?: number): void {
BackendService.sendModeratorCommand('Command_BanFromServer', {
BackendService.sendModeratorCommand(Command_BanFromServer_ext, create(Command_BanFromServerSchema, {
minutes, userName, address, reason, visibleReason, clientid, removeMessages
}, {
}), {
onSuccess: () => {
ModeratorPersistence.banFromServer(userName);
},

View file

@ -1,8 +1,13 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import {
Command_ForceActivateUser_ext, Command_ForceActivateUserSchema,
} from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
export function forceActivateUser(usernameToActivate: string, moderatorName: string): void {
BackendService.sendModeratorCommand('Command_ForceActivateUser', { usernameToActivate, moderatorName }, {
const cmd = create(Command_ForceActivateUserSchema, { usernameToActivate, moderatorName });
BackendService.sendModeratorCommand(Command_ForceActivateUser_ext, cmd, {
onSuccess: () => {
ModeratorPersistence.forceActivateUser(usernameToActivate, moderatorName);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GetAdminNotes_ext, Command_GetAdminNotesSchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
import { Response_GetAdminNotes_ext } from 'generated/proto/response_get_admin_notes_pb';
export function getAdminNotes(userName: string): void {
BackendService.sendModeratorCommand('Command_GetAdminNotes', { userName }, {
responseName: 'Response_GetAdminNotes',
BackendService.sendModeratorCommand(Command_GetAdminNotes_ext, create(Command_GetAdminNotesSchema, { userName }), {
responseExt: Response_GetAdminNotes_ext,
onSuccess: (response) => {
ModeratorPersistence.getAdminNotes(userName, response.notes);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GetBanHistory_ext, Command_GetBanHistorySchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
import { Response_BanHistory_ext } from 'generated/proto/response_ban_history_pb';
export function getBanHistory(userName: string): void {
BackendService.sendModeratorCommand('Command_GetBanHistory', { userName }, {
responseName: 'Response_BanHistory',
BackendService.sendModeratorCommand(Command_GetBanHistory_ext, create(Command_GetBanHistorySchema, { userName }), {
responseExt: Response_BanHistory_ext,
onSuccess: (response) => {
ModeratorPersistence.banHistory(userName, response.banList);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GetWarnHistory_ext, Command_GetWarnHistorySchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
import { Response_WarnHistory_ext } from 'generated/proto/response_warn_history_pb';
export function getWarnHistory(userName: string): void {
BackendService.sendModeratorCommand('Command_GetWarnHistory', { userName }, {
responseName: 'Response_WarnHistory',
BackendService.sendModeratorCommand(Command_GetWarnHistory_ext, create(Command_GetWarnHistorySchema, { userName }), {
responseExt: Response_WarnHistory_ext,
onSuccess: (response) => {
ModeratorPersistence.warnHistory(userName, response.warnList);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GetWarnList_ext, Command_GetWarnListSchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
import { Response_WarnList_ext } from 'generated/proto/response_warn_list_pb';
export function getWarnList(modName: string, userName: string, userClientid: string): void {
BackendService.sendModeratorCommand('Command_GetWarnList', { modName, userName, userClientid }, {
responseName: 'Response_WarnList',
BackendService.sendModeratorCommand(Command_GetWarnList_ext, create(Command_GetWarnListSchema, { modName, userName, userClientid }), {
responseExt: Response_WarnList_ext,
onSuccess: (response) => {
ModeratorPersistence.warnListOptions(response.warning);
},

View file

@ -1,8 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import {
Command_GrantReplayAccess_ext, Command_GrantReplayAccessSchema,
} from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
export function grantReplayAccess(replayId: number, moderatorName: string): void {
BackendService.sendModeratorCommand('Command_GrantReplayAccess', { replayId, moderatorName }, {
BackendService.sendModeratorCommand(Command_GrantReplayAccess_ext, create(Command_GrantReplayAccessSchema, { replayId, moderatorName }), {
onSuccess: () => {
ModeratorPersistence.grantReplayAccess(replayId, moderatorName);
},

View file

@ -22,6 +22,23 @@ vi.mock('../../persistence', () => ({
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import { BackendService } from '../../services/BackendService';
import { ModeratorPersistence } from '../../persistence';
import {
Command_BanFromServer_ext,
Command_ForceActivateUser_ext,
Command_GetAdminNotes_ext,
Command_GetBanHistory_ext,
Command_GetWarnHistory_ext,
Command_GetWarnList_ext,
Command_GrantReplayAccess_ext,
Command_UpdateAdminNotes_ext,
Command_ViewLogHistory_ext,
Command_WarnUser_ext,
} from 'generated/proto/moderator_commands_pb';
import { Response_GetAdminNotes_ext } from 'generated/proto/response_get_admin_notes_pb';
import { Response_BanHistory_ext } from 'generated/proto/response_ban_history_pb';
import { Response_WarnHistory_ext } from 'generated/proto/response_warn_history_pb';
import { Response_WarnList_ext } from 'generated/proto/response_warn_list_pb';
import { Response_ViewLogHistory_ext } from 'generated/proto/response_viewlog_history_pb';
import { banFromServer } from './banFromServer';
import { forceActivateUser } from './forceActivateUser';
import { getAdminNotes } from './getAdminNotes';
@ -34,7 +51,8 @@ import { viewLogHistory } from './viewLogHistory';
import { warnUser } from './warnUser';
const { getLastSendOpts, invokeOnSuccess } = makeCallbackHelpers(
BackendService.sendModeratorCommand as vi.Mock
BackendService.sendModeratorCommand as vi.Mock,
2
);
beforeEach(() => vi.clearAllMocks());
@ -47,7 +65,7 @@ describe('banFromServer', () => {
it('calls sendModeratorCommand with Command_BanFromServer', () => {
banFromServer(30, 'alice', '1.2.3.4', 'reason', 'visible', 'cid', 1);
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(
'Command_BanFromServer',
Command_BanFromServer_ext,
expect.objectContaining({ minutes: 30, userName: 'alice' }),
expect.any(Object)
);
@ -67,7 +85,7 @@ describe('forceActivateUser', () => {
it('calls sendModeratorCommand with Command_ForceActivateUser', () => {
forceActivateUser('alice', 'mod1');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith('Command_ForceActivateUser', expect.any(Object), expect.any(Object));
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(Command_ForceActivateUser_ext, expect.any(Object), expect.any(Object));
});
it('onSuccess calls ModeratorPersistence.forceActivateUser', () => {
@ -85,16 +103,16 @@ describe('getAdminNotes', () => {
it('calls sendModeratorCommand with Command_GetAdminNotes', () => {
getAdminNotes('alice');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(
'Command_GetAdminNotes',
Command_GetAdminNotes_ext,
expect.any(Object),
expect.objectContaining({ responseName: 'Response_GetAdminNotes' })
expect.objectContaining({ responseExt: Response_GetAdminNotes_ext })
);
});
it('onSuccess calls ModeratorPersistence.getAdminNotes with notes', () => {
getAdminNotes('alice');
const resp = { notes: 'some notes' };
invokeOnSuccess(resp, { responseCode: 0, '.Response_GetAdminNotes.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.getAdminNotes).toHaveBeenCalledWith('alice', 'some notes');
});
});
@ -107,16 +125,16 @@ describe('getBanHistory', () => {
it('calls sendModeratorCommand with Command_GetBanHistory', () => {
getBanHistory('alice');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(
'Command_GetBanHistory',
Command_GetBanHistory_ext,
expect.any(Object),
expect.objectContaining({ responseName: 'Response_BanHistory' })
expect.objectContaining({ responseExt: Response_BanHistory_ext })
);
});
it('onSuccess calls ModeratorPersistence.banHistory with banList', () => {
getBanHistory('alice');
const resp = { banList: [{ id: 1 }] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_BanHistory.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.banHistory).toHaveBeenCalledWith('alice', [{ id: 1 }]);
});
});
@ -129,16 +147,16 @@ describe('getWarnHistory', () => {
it('calls sendModeratorCommand with Command_GetWarnHistory', () => {
getWarnHistory('alice');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(
'Command_GetWarnHistory',
Command_GetWarnHistory_ext,
expect.any(Object),
expect.objectContaining({ responseName: 'Response_WarnHistory' })
expect.objectContaining({ responseExt: Response_WarnHistory_ext })
);
});
it('onSuccess calls ModeratorPersistence.warnHistory with warnList', () => {
getWarnHistory('alice');
const resp = { warnList: [{ id: 2 }] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_WarnHistory.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.warnHistory).toHaveBeenCalledWith('alice', [{ id: 2 }]);
});
});
@ -151,16 +169,16 @@ describe('getWarnList', () => {
it('calls sendModeratorCommand with Command_GetWarnList', () => {
getWarnList('mod1', 'alice', 'US');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(
'Command_GetWarnList',
Command_GetWarnList_ext,
expect.any(Object),
expect.objectContaining({ responseName: 'Response_WarnList' })
expect.objectContaining({ responseExt: Response_WarnList_ext })
);
});
it('onSuccess calls ModeratorPersistence.warnListOptions with warning', () => {
getWarnList('mod1', 'alice', 'US');
const resp = { warning: ['w1', 'w2'] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_WarnList.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.warnListOptions).toHaveBeenCalledWith(['w1', 'w2']);
});
});
@ -172,7 +190,7 @@ describe('grantReplayAccess', () => {
it('calls sendModeratorCommand with Command_GrantReplayAccess', () => {
grantReplayAccess(10, 'mod1');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith('Command_GrantReplayAccess', expect.any(Object), expect.any(Object));
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(Command_GrantReplayAccess_ext, expect.any(Object), expect.any(Object));
});
it('onSuccess calls ModeratorPersistence.grantReplayAccess', () => {
@ -189,7 +207,7 @@ describe('updateAdminNotes', () => {
it('calls sendModeratorCommand with Command_UpdateAdminNotes', () => {
updateAdminNotes('alice', 'new notes');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith('Command_UpdateAdminNotes', expect.any(Object), expect.any(Object));
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(Command_UpdateAdminNotes_ext, expect.any(Object), expect.any(Object));
});
it('onSuccess calls ModeratorPersistence.updateAdminNotes', () => {
@ -205,18 +223,18 @@ describe('updateAdminNotes', () => {
describe('viewLogHistory', () => {
it('calls sendModeratorCommand with Command_ViewLogHistory', () => {
viewLogHistory({ filters: 'all' } as any);
viewLogHistory({ dateRange: 7 } as any);
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(
'Command_ViewLogHistory',
Command_ViewLogHistory_ext,
expect.any(Object),
expect.objectContaining({ responseName: 'Response_ViewLogHistory' })
expect.objectContaining({ responseExt: Response_ViewLogHistory_ext })
);
});
it('onSuccess calls ModeratorPersistence.viewLogs with logMessage', () => {
viewLogHistory({ filters: 'all' } as any);
viewLogHistory({ dateRange: 7 } as any);
const resp = { logMessage: ['log1'] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_ViewLogHistory.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(ModeratorPersistence.viewLogs).toHaveBeenCalledWith(['log1']);
});
});
@ -228,7 +246,7 @@ describe('warnUser', () => {
it('calls sendModeratorCommand with Command_WarnUser', () => {
warnUser('alice', 'bad behavior', 'cid');
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith('Command_WarnUser', expect.any(Object), expect.any(Object));
expect(BackendService.sendModeratorCommand).toHaveBeenCalledWith(Command_WarnUser_ext, expect.any(Object), expect.any(Object));
});
it('onSuccess calls ModeratorPersistence.warnUser', () => {

View file

@ -1,8 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import {
Command_UpdateAdminNotes_ext, Command_UpdateAdminNotesSchema,
} from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
export function updateAdminNotes(userName: string, notes: string): void {
BackendService.sendModeratorCommand('Command_UpdateAdminNotes', { userName, notes }, {
BackendService.sendModeratorCommand(Command_UpdateAdminNotes_ext, create(Command_UpdateAdminNotesSchema, { userName, notes }), {
onSuccess: () => {
ModeratorPersistence.updateAdminNotes(userName, notes);
},

View file

@ -1,10 +1,13 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ViewLogHistory_ext, Command_ViewLogHistorySchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
import { Response_ViewLogHistory_ext } from 'generated/proto/response_viewlog_history_pb';
import { LogFilters } from 'types';
export function viewLogHistory(filters: LogFilters): void {
BackendService.sendModeratorCommand('Command_ViewLogHistory', filters, {
responseName: 'Response_ViewLogHistory',
BackendService.sendModeratorCommand(Command_ViewLogHistory_ext, create(Command_ViewLogHistorySchema, filters), {
responseExt: Response_ViewLogHistory_ext,
onSuccess: (response) => {
ModeratorPersistence.viewLogs(response.logMessage);
},

View file

@ -1,8 +1,11 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_WarnUser_ext, Command_WarnUserSchema } from 'generated/proto/moderator_commands_pb';
import { ModeratorPersistence } from '../../persistence';
export function warnUser(userName: string, reason: string, clientid?: string, removeMessages?: number): void {
BackendService.sendModeratorCommand('Command_WarnUser', { userName, reason, clientid, removeMessages }, {
const cmd = create(Command_WarnUserSchema, { userName, reason, clientid, removeMessages });
BackendService.sendModeratorCommand(Command_WarnUser_ext, cmd, {
onSuccess: () => {
ModeratorPersistence.warnUser(userName);
},

View file

@ -1,9 +1,11 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_CreateGame_ext, Command_CreateGameSchema } from 'generated/proto/room_commands_pb';
import { RoomPersistence } from '../../persistence';
import { GameConfig } from 'types';
export function createGame(roomId: number, gameConfig: GameConfig): void {
BackendService.sendRoomCommand(roomId, 'Command_CreateGame', gameConfig, {
BackendService.sendRoomCommand(roomId, Command_CreateGame_ext, create(Command_CreateGameSchema, gameConfig), {
onSuccess: () => {
RoomPersistence.gameCreated(roomId);
},

View file

@ -1,9 +1,11 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_JoinGame_ext, Command_JoinGameSchema } from 'generated/proto/room_commands_pb';
import { RoomPersistence } from '../../persistence';
import { JoinGameParams } from 'types';
export function joinGame(roomId: number, joinGameParams: JoinGameParams): void {
BackendService.sendRoomCommand(roomId, 'Command_JoinGame', joinGameParams, {
BackendService.sendRoomCommand(roomId, Command_JoinGame_ext, create(Command_JoinGameSchema, joinGameParams), {
onSuccess: () => {
RoomPersistence.joinedGame(roomId, joinGameParams.gameId);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_LeaveRoom_ext, Command_LeaveRoomSchema } from 'generated/proto/room_commands_pb';
import { RoomPersistence } from '../../persistence';
export function leaveRoom(roomId: number): void {
BackendService.sendRoomCommand(roomId, 'Command_LeaveRoom', {}, {
BackendService.sendRoomCommand(roomId, Command_LeaveRoom_ext, create(Command_LeaveRoomSchema), {
onSuccess: () => {
RoomPersistence.leaveRoom(roomId);
},

View file

@ -15,6 +15,7 @@ vi.mock('../../persistence', () => ({
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import { BackendService } from '../../services/BackendService';
import { RoomPersistence } from '../../persistence';
import { Command_CreateGame_ext, Command_JoinGame_ext, Command_LeaveRoom_ext, Command_RoomSay_ext } from 'generated/proto/room_commands_pb';
import { createGame } from './createGame';
import { joinGame } from './joinGame';
import { leaveRoom } from './leaveRoom';
@ -22,7 +23,7 @@ import { roomSay } from './roomSay';
const { getLastSendOpts, invokeOnSuccess } = makeCallbackHelpers(
BackendService.sendRoomCommand as vi.Mock,
// sendRoomCommand(roomId, commandName, params, options) — options at index 3
// sendRoomCommand(roomId, ext, value, options) — options at index 3
3
);
@ -35,7 +36,9 @@ describe('createGame', () => {
it('calls sendRoomCommand with Command_CreateGame', () => {
createGame(5, { maxPlayers: 4 } as any);
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(5, 'Command_CreateGame', { maxPlayers: 4 }, expect.any(Object));
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(
5, Command_CreateGame_ext, expect.objectContaining({ maxPlayers: 4 }), expect.any(Object)
);
});
it('onSuccess calls RoomPersistence.gameCreated with roomId', () => {
@ -52,7 +55,9 @@ describe('joinGame', () => {
it('calls sendRoomCommand with Command_JoinGame', () => {
joinGame(7, { gameId: 42, password: '' } as any);
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(7, 'Command_JoinGame', { gameId: 42, password: '' }, expect.any(Object));
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(
7, Command_JoinGame_ext, expect.objectContaining({ gameId: 42, password: '' }), expect.any(Object)
);
});
it('onSuccess calls RoomPersistence.joinedGame with roomId and gameId', () => {
@ -69,7 +74,7 @@ describe('leaveRoom', () => {
it('calls sendRoomCommand with Command_LeaveRoom', () => {
leaveRoom(3);
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(3, 'Command_LeaveRoom', {}, expect.any(Object));
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(3, Command_LeaveRoom_ext, expect.any(Object), expect.any(Object));
});
it('onSuccess calls RoomPersistence.leaveRoom with roomId', () => {
@ -86,7 +91,11 @@ describe('roomSay', () => {
it('calls sendRoomCommand with trimmed message', () => {
roomSay(2, ' hello ');
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(2, 'Command_RoomSay', { message: 'hello' }, expect.any(Object));
expect(BackendService.sendRoomCommand).toHaveBeenCalledWith(
2,
Command_RoomSay_ext,
expect.objectContaining({ message: 'hello' })
);
});
it('does not call sendRoomCommand when message is blank', () => {

View file

@ -1,4 +1,6 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_RoomSay_ext, Command_RoomSaySchema } from 'generated/proto/room_commands_pb';
export function roomSay(roomId: number, message: string): void {
const trimmed = message.trim();
@ -7,5 +9,5 @@ export function roomSay(roomId: number, message: string): void {
return;
}
BackendService.sendRoomCommand(roomId, 'Command_RoomSay', { message: trimmed }, {});
BackendService.sendRoomCommand(roomId, Command_RoomSay_ext, create(Command_RoomSaySchema, { message: trimmed }));
}

View file

@ -1,8 +1,11 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_AccountEdit_ext, Command_AccountEditSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
export function accountEdit(passwordCheck: string, realName?: string, email?: string, country?: string): void {
BackendService.sendSessionCommand('Command_AccountEdit', { passwordCheck, realName, email, country }, {
const cmd = create(Command_AccountEditSchema, { passwordCheck, realName, email, country });
BackendService.sendSessionCommand(Command_AccountEdit_ext, cmd, {
onSuccess: () => {
SessionPersistence.accountEditChanged(realName, email, country);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_AccountImage_ext, Command_AccountImageSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
export function accountImage(image: Uint8Array): void {
BackendService.sendSessionCommand('Command_AccountImage', { image }, {
BackendService.sendSessionCommand(Command_AccountImage_ext, create(Command_AccountImageSchema, { image }), {
onSuccess: () => {
SessionPersistence.accountImageChanged(image);
},

View file

@ -1,8 +1,11 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_AccountPassword_ext, Command_AccountPasswordSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
export function accountPassword(oldPassword: string, newPassword: string, hashedNewPassword: string): void {
BackendService.sendSessionCommand('Command_AccountPassword', { oldPassword, newPassword, hashedNewPassword }, {
const cmd = create(Command_AccountPasswordSchema, { oldPassword, newPassword, hashedNewPassword });
BackendService.sendSessionCommand(Command_AccountPassword_ext, cmd, {
onSuccess: () => {
SessionPersistence.accountPasswordChange();
},

View file

@ -1,23 +1,25 @@
import { AccountActivationParams } from 'store';
import { StatusEnum, WebSocketConnectOptions } from 'types';
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import { ProtoController } from '../../services/ProtoController';
import { Command_Activate_ext, Command_ActivateSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { Response_ResponseCode } from 'generated/proto/response_pb';
import { disconnect, login, updateStatus } from './';
export function activate(options: WebSocketConnectOptions, password?: string, passwordSalt?: string): void {
const { userName, token } = options as unknown as AccountActivationParams;
BackendService.sendSessionCommand('Command_Activate', {
BackendService.sendSessionCommand(Command_Activate_ext, create(Command_ActivateSchema, {
...webClient.clientConfig,
userName,
token,
}, {
}), {
onResponseCode: {
[ProtoController.root.Response.ResponseCode.RespActivationAccepted]: () => {
[Response_ResponseCode.RespActivationAccepted]: () => {
SessionPersistence.accountActivationSuccess();
login(options, password, passwordSalt);
},

View file

@ -1,4 +1,6 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_AddToList_ext, Command_AddToListSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
export function addToBuddyList(userName: string): void {
@ -10,7 +12,7 @@ export function addToIgnoreList(userName: string): void {
}
export function addToList(list: string, userName: string): void {
BackendService.sendSessionCommand('Command_AddToList', { list, userName }, {
BackendService.sendSessionCommand(Command_AddToList_ext, create(Command_AddToListSchema, { list, userName }), {
onSuccess: () => {
SessionPersistence.addToList(list, userName);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeckDelSchema, Command_DeckDel_ext } from 'generated/proto/command_deck_del_pb';
import { SessionPersistence } from '../../persistence';
export function deckDel(deckId: number): void {
BackendService.sendSessionCommand('Command_DeckDel', { deckId }, {
BackendService.sendSessionCommand(Command_DeckDel_ext, create(Command_DeckDelSchema, { deckId }), {
onSuccess: () => {
SessionPersistence.deleteServerDeck(deckId);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeckDelDirSchema, Command_DeckDelDir_ext } from 'generated/proto/command_deck_del_dir_pb';
import { SessionPersistence } from '../../persistence';
export function deckDelDir(path: string): void {
BackendService.sendSessionCommand('Command_DeckDelDir', { path }, {
BackendService.sendSessionCommand(Command_DeckDelDir_ext, create(Command_DeckDelDirSchema, { path }), {
onSuccess: () => {
SessionPersistence.deleteServerDeckDir(path);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeckListSchema, Command_DeckList_ext } from 'generated/proto/command_deck_list_pb';
import { SessionPersistence } from '../../persistence';
import { Response_DeckList_ext } from 'generated/proto/response_deck_list_pb';
export function deckList(): void {
BackendService.sendSessionCommand('Command_DeckList', {}, {
responseName: 'Response_DeckList',
BackendService.sendSessionCommand(Command_DeckList_ext, create(Command_DeckListSchema), {
responseExt: Response_DeckList_ext,
onSuccess: (response) => {
SessionPersistence.updateServerDecks(response);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeckNewDirSchema, Command_DeckNewDir_ext } from 'generated/proto/command_deck_new_dir_pb';
import { SessionPersistence } from '../../persistence';
export function deckNewDir(path: string, dirName: string): void {
BackendService.sendSessionCommand('Command_DeckNewDir', { path, dirName }, {
BackendService.sendSessionCommand(Command_DeckNewDir_ext, create(Command_DeckNewDirSchema, { path, dirName }), {
onSuccess: () => {
SessionPersistence.createServerDeckDir(path, dirName);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_DeckUploadSchema, Command_DeckUpload_ext } from 'generated/proto/command_deck_upload_pb';
import { SessionPersistence } from '../../persistence';
import { Response_DeckUpload_ext } from 'generated/proto/response_deck_upload_pb';
export function deckUpload(path: string, deckId: number, deckList: string): void {
BackendService.sendSessionCommand('Command_DeckUpload', { path, deckId, deckList }, {
responseName: 'Response_DeckUpload',
BackendService.sendSessionCommand(Command_DeckUpload_ext, create(Command_DeckUploadSchema, { path, deckId, deckList }), {
responseExt: Response_DeckUpload_ext,
onSuccess: (response) => {
SessionPersistence.uploadServerDeck(path, response.newFile);
},

View file

@ -1,19 +1,23 @@
import { ForgotPasswordChallengeParams } from 'store';
import { StatusEnum, WebSocketConnectOptions } from 'types';
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import {
Command_ForgotPasswordChallenge_ext, Command_ForgotPasswordChallengeSchema,
} from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { disconnect, updateStatus } from './';
export function forgotPasswordChallenge(options: WebSocketConnectOptions): void {
const { userName, email } = options as unknown as ForgotPasswordChallengeParams;
BackendService.sendSessionCommand('Command_ForgotPasswordChallenge', {
BackendService.sendSessionCommand(Command_ForgotPasswordChallenge_ext, create(Command_ForgotPasswordChallengeSchema, {
...webClient.clientConfig,
userName,
email,
}, {
}), {
onSuccess: () => {
updateStatus(StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPassword();

View file

@ -1,20 +1,25 @@
import { ForgotPasswordParams } from 'store';
import { StatusEnum, WebSocketConnectOptions } from 'types';
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import {
Command_ForgotPasswordRequest_ext, Command_ForgotPasswordRequestSchema,
} from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { Response_ForgotPasswordRequest_ext } from 'generated/proto/response_forgotpasswordrequest_pb';
import { disconnect, updateStatus } from './';
export function forgotPasswordRequest(options: WebSocketConnectOptions): void {
const { userName } = options as unknown as ForgotPasswordParams;
BackendService.sendSessionCommand('Command_ForgotPasswordRequest', {
BackendService.sendSessionCommand(Command_ForgotPasswordRequest_ext, create(Command_ForgotPasswordRequestSchema, {
...webClient.clientConfig,
userName,
}, {
responseName: 'Response_ForgotPasswordRequest',
}), {
responseExt: Response_ForgotPasswordRequest_ext,
onSuccess: (resp) => {
if (resp?.challengeEmail) {
updateStatus(StatusEnum.DISCONNECTED, null);

View file

@ -1,8 +1,13 @@
import { ForgotPasswordResetParams } from 'store';
import { StatusEnum, WebSocketConnectOptions } from 'types';
import { create } from '@bufbuild/protobuf';
import type { MessageInitShape } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import {
Command_ForgotPasswordReset_ext, Command_ForgotPasswordResetSchema,
} from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { hashPassword } from '../../utils';
@ -11,19 +16,16 @@ import { disconnect, updateStatus } from '.';
export function forgotPasswordReset(options: WebSocketConnectOptions, newPassword?: string, passwordSalt?: string): void {
const { userName, token } = options as unknown as ForgotPasswordResetParams;
const params: any = {
const params: MessageInitShape<typeof Command_ForgotPasswordResetSchema> = {
...webClient.clientConfig,
userName,
token,
...(passwordSalt
? { hashedNewPassword: hashPassword(passwordSalt, newPassword) }
: { newPassword }),
};
if (passwordSalt) {
params.hashedNewPassword = hashPassword(passwordSalt, newPassword);
} else {
params.newPassword = newPassword;
}
BackendService.sendSessionCommand('Command_ForgotPasswordReset', params, {
BackendService.sendSessionCommand(Command_ForgotPasswordReset_ext, create(Command_ForgotPasswordResetSchema, params), {
onSuccess: () => {
updateStatus(StatusEnum.DISCONNECTED, null);
SessionPersistence.resetPasswordSuccess();

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GetGamesOfUser_ext, Command_GetGamesOfUserSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { Response_GetGamesOfUser_ext } from 'generated/proto/response_get_games_of_user_pb';
export function getGamesOfUser(userName: string): void {
BackendService.sendSessionCommand('Command_GetGamesOfUser', { userName }, {
responseName: 'Response_GetGamesOfUser',
BackendService.sendSessionCommand(Command_GetGamesOfUser_ext, create(Command_GetGamesOfUserSchema, { userName }), {
responseExt: Response_GetGamesOfUser_ext,
onSuccess: (response) => {
SessionPersistence.getGamesOfUser(userName, response);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_GetUserInfo_ext, Command_GetUserInfoSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { Response_GetUserInfo_ext } from 'generated/proto/response_get_user_info_pb';
export function getUserInfo(userName: string): void {
BackendService.sendSessionCommand('Command_GetUserInfo', { userName }, {
responseName: 'Response_GetUserInfo',
BackendService.sendSessionCommand(Command_GetUserInfo_ext, create(Command_GetUserInfoSchema, { userName }), {
responseExt: Response_GetUserInfo_ext,
onSuccess: (response) => {
SessionPersistence.getUserInfo(response.userInfo);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_JoinRoom_ext, Command_JoinRoomSchema } from 'generated/proto/session_commands_pb';
import { RoomPersistence } from '../../persistence';
import { Response_JoinRoom_ext } from 'generated/proto/response_join_room_pb';
export function joinRoom(roomId: number): void {
BackendService.sendSessionCommand('Command_JoinRoom', { roomId }, {
responseName: 'Response_JoinRoom',
BackendService.sendSessionCommand(Command_JoinRoom_ext, create(Command_JoinRoomSchema, { roomId }), {
responseExt: Response_JoinRoom_ext,
onSuccess: (response) => {
RoomPersistence.joinRoom(response.roomInfo);
},

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ListRooms_ext, Command_ListRoomsSchema } from 'generated/proto/session_commands_pb';
export function listRooms(): void {
BackendService.sendSessionCommand('Command_ListRooms', {}, {});
BackendService.sendSessionCommand(Command_ListRooms_ext, create(Command_ListRoomsSchema));
}

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ListUsers_ext, Command_ListUsersSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { Response_ListUsers_ext } from 'generated/proto/response_list_users_pb';
export function listUsers(): void {
BackendService.sendSessionCommand('Command_ListUsers', {}, {
responseName: 'Response_ListUsers',
BackendService.sendSessionCommand(Command_ListUsers_ext, create(Command_ListUsersSchema), {
responseExt: Response_ListUsers_ext,
onSuccess: (response) => {
SessionPersistence.updateUsers(response.userList);
},

View file

@ -1,9 +1,13 @@
import { StatusEnum, WebSocketConnectOptions } from 'types';
import { create } from '@bufbuild/protobuf';
import type { MessageInitShape } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import { ProtoController } from '../../services/ProtoController';
import { Command_Login_ext, Command_LoginSchema } from 'generated/proto/session_commands_pb';
import { hashPassword } from '../../utils';
import { SessionPersistence } from '../../persistence';
import { Response_Login_ext } from 'generated/proto/response_login_pb';
import { Response_ResponseCode } from 'generated/proto/response_pb';
import {
disconnect,
@ -15,20 +19,15 @@ import {
export function login(options: WebSocketConnectOptions, password?: string, passwordSalt?: string): void {
const { userName, hashedPassword } = options;
const loginConfig: any = {
const loginConfig: MessageInitShape<typeof Command_LoginSchema> = {
...webClient.clientConfig,
clientid: 'webatrice',
userName,
...(passwordSalt
? { hashedPassword: hashedPassword || hashPassword(passwordSalt, password) }
: { password }),
};
if (passwordSalt) {
loginConfig.hashedPassword = hashedPassword || hashPassword(passwordSalt, password);
} else {
loginConfig.password = password;
}
const { ResponseCode } = ProtoController.root.Response;
const onLoginError = (message: string, extra?: () => void) => {
updateStatus(StatusEnum.DISCONNECTED, message);
extra?.();
@ -36,8 +35,8 @@ export function login(options: WebSocketConnectOptions, password?: string, passw
disconnect();
};
BackendService.sendSessionCommand('Command_Login', loginConfig, {
responseName: 'Response_Login',
BackendService.sendSessionCommand(Command_Login_ext, create(Command_LoginSchema, loginConfig), {
responseExt: Response_Login_ext,
onSuccess: (resp) => {
const { buddyList, ignoreList, userInfo } = resp;
@ -53,23 +52,23 @@ export function login(options: WebSocketConnectOptions, password?: string, passw
updateStatus(StatusEnum.LOGGED_IN, 'Logged in.');
},
onResponseCode: {
[ResponseCode.RespClientUpdateRequired]: () =>
[Response_ResponseCode.RespClientUpdateRequired]: () =>
onLoginError('Login failed: missing features'),
[ResponseCode.RespWrongPassword]: () =>
[Response_ResponseCode.RespWrongPassword]: () =>
onLoginError('Login failed: incorrect username or password'),
[ResponseCode.RespUsernameInvalid]: () =>
[Response_ResponseCode.RespUsernameInvalid]: () =>
onLoginError('Login failed: incorrect username or password'),
[ResponseCode.RespWouldOverwriteOldSession]: () =>
[Response_ResponseCode.RespWouldOverwriteOldSession]: () =>
onLoginError('Login failed: duplicated user session'),
[ResponseCode.RespUserIsBanned]: () =>
[Response_ResponseCode.RespUserIsBanned]: () =>
onLoginError('Login failed: banned user'),
[ResponseCode.RespRegistrationRequired]: () =>
[Response_ResponseCode.RespRegistrationRequired]: () =>
onLoginError('Login failed: registration required'),
[ResponseCode.RespClientIdRequired]: () =>
[Response_ResponseCode.RespClientIdRequired]: () =>
onLoginError('Login failed: missing client ID'),
[ResponseCode.RespContextError]: () =>
[Response_ResponseCode.RespContextError]: () =>
onLoginError('Login failed: server error'),
[ResponseCode.RespAccountNotActivated]: () =>
[Response_ResponseCode.RespAccountNotActivated]: () =>
onLoginError('Login failed: account not activated',
() => {
const { password: _p, newPassword: _np, ...safeOptions } = options;

View file

@ -1,5 +1,7 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_Message_ext, Command_MessageSchema } from 'generated/proto/session_commands_pb';
export function message(userName: string, message: string): void {
BackendService.sendSessionCommand('Command_Message', { userName, message }, {});
BackendService.sendSessionCommand(Command_Message_ext, create(Command_MessageSchema, { userName, message }));
}

View file

@ -1,7 +1,9 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_Ping_ext, Command_PingSchema } from 'generated/proto/session_commands_pb';
export function ping(pingReceived: Function): void {
BackendService.sendSessionCommand('Command_Ping', {}, {
BackendService.sendSessionCommand(Command_Ping_ext, create(Command_PingSchema), {
onResponse: (raw) => pingReceived(raw),
});
}

View file

@ -1,73 +1,71 @@
import { ServerRegisterParams } from 'store';
import { StatusEnum, WebSocketConnectOptions } from 'types';
import { create } from '@bufbuild/protobuf';
import type { MessageInitShape } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import { ProtoController } from '../../services/ProtoController';
import { Command_Register_ext, Command_RegisterSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { hashPassword } from '../../utils';
import { Response_ResponseCode } from 'generated/proto/response_pb';
import { login, disconnect, updateStatus } from './';
export function register(options: WebSocketConnectOptions, password?: string, passwordSalt?: string): void {
const { userName, email, country, realName } = options as ServerRegisterParams;
const params: any = {
const params: MessageInitShape<typeof Command_RegisterSchema> = {
...webClient.clientConfig,
userName,
email,
country,
realName,
...(passwordSalt
? { hashedPassword: hashPassword(passwordSalt, password) }
: { password }),
};
if (passwordSalt) {
params.hashedPassword = hashPassword(passwordSalt, password);
} else {
params.password = password;
}
const { ResponseCode } = ProtoController.root.Response;
const onRegistrationError = (action: () => void) => {
action();
updateStatus(StatusEnum.DISCONNECTED, 'Registration failed');
disconnect();
};
BackendService.sendSessionCommand('Command_Register', params, {
BackendService.sendSessionCommand(Command_Register_ext, create(Command_RegisterSchema, params), {
onResponseCode: {
[ResponseCode.RespRegistrationAccepted]: () => {
[Response_ResponseCode.RespRegistrationAccepted]: () => {
login(options, password, passwordSalt);
SessionPersistence.registrationSuccess();
},
[ResponseCode.RespRegistrationAcceptedNeedsActivation]: () => {
[Response_ResponseCode.RespRegistrationAcceptedNeedsActivation]: () => {
updateStatus(StatusEnum.DISCONNECTED, 'Registration accepted, awaiting activation');
const { password: _p, newPassword: _np, ...safeOptions } = options;
SessionPersistence.accountAwaitingActivation(safeOptions);
disconnect();
},
[ResponseCode.RespUserAlreadyExists]: () => onRegistrationError(
[Response_ResponseCode.RespUserAlreadyExists]: () => onRegistrationError(
() => SessionPersistence.registrationUserNameError('Username is taken')
),
[ResponseCode.RespUsernameInvalid]: () => onRegistrationError(
[Response_ResponseCode.RespUsernameInvalid]: () => onRegistrationError(
() => SessionPersistence.registrationUserNameError('Invalid username')
),
[ResponseCode.RespPasswordTooShort]: () => onRegistrationError(
[Response_ResponseCode.RespPasswordTooShort]: () => onRegistrationError(
() => SessionPersistence.registrationPasswordError('Your password was too short')
),
[ResponseCode.RespEmailRequiredToRegister]: () => onRegistrationError(
[Response_ResponseCode.RespEmailRequiredToRegister]: () => onRegistrationError(
() => SessionPersistence.registrationRequiresEmail()
),
[ResponseCode.RespEmailBlackListed]: () => onRegistrationError(
[Response_ResponseCode.RespEmailBlackListed]: () => onRegistrationError(
() => SessionPersistence.registrationEmailError('This email provider has been blocked')
),
[ResponseCode.RespTooManyRequests]: () => onRegistrationError(
[Response_ResponseCode.RespTooManyRequests]: () => onRegistrationError(
() => SessionPersistence.registrationEmailError('Max accounts reached for this email')
),
[ResponseCode.RespRegistrationDisabled]: () => onRegistrationError(
[Response_ResponseCode.RespRegistrationDisabled]: () => onRegistrationError(
() => SessionPersistence.registrationFailed('Registration is currently disabled')
),
[ResponseCode.RespUserIsBanned]: (raw) => onRegistrationError(
[Response_ResponseCode.RespUserIsBanned]: (raw) => onRegistrationError(
() => SessionPersistence.registrationFailed(raw.reasonStr, raw.endTime)
),
},

View file

@ -1,4 +1,6 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_RemoveFromList_ext, Command_RemoveFromListSchema } from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
export function removeFromBuddyList(userName: string): void {
@ -10,7 +12,7 @@ export function removeFromIgnoreList(userName: string): void {
}
export function removeFromList(list: string, userName: string): void {
BackendService.sendSessionCommand('Command_RemoveFromList', { list, userName }, {
BackendService.sendSessionCommand(Command_RemoveFromList_ext, create(Command_RemoveFromListSchema, { list, userName }), {
onSuccess: () => {
SessionPersistence.removeFromList(list, userName);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReplayDeleteMatchSchema, Command_ReplayDeleteMatch_ext } from 'generated/proto/command_replay_delete_match_pb';
import { SessionPersistence } from '../../persistence';
export function replayDeleteMatch(gameId: number): void {
BackendService.sendSessionCommand('Command_ReplayDeleteMatch', { gameId }, {
BackendService.sendSessionCommand(Command_ReplayDeleteMatch_ext, create(Command_ReplayDeleteMatchSchema, { gameId }), {
onSuccess: () => {
SessionPersistence.replayDeleteMatch(gameId);
},

View file

@ -1,8 +1,11 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReplayGetCodeSchema, Command_ReplayGetCode_ext } from 'generated/proto/command_replay_get_code_pb';
import { Response_ReplayGetCode_ext } from 'generated/proto/response_replay_get_code_pb';
export function replayGetCode(gameId: number, onCodeReceived: (code: string) => void): void {
BackendService.sendSessionCommand('Command_ReplayGetCode', { gameId }, {
responseName: 'Response_ReplayGetCode',
BackendService.sendSessionCommand(Command_ReplayGetCode_ext, create(Command_ReplayGetCodeSchema, { gameId }), {
responseExt: Response_ReplayGetCode_ext,
onSuccess: (response) => {
onCodeReceived(response.replayCode);
},

View file

@ -1,9 +1,12 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReplayListSchema, Command_ReplayList_ext } from 'generated/proto/command_replay_list_pb';
import { SessionPersistence } from '../../persistence';
import { Response_ReplayList_ext } from 'generated/proto/response_replay_list_pb';
export function replayList(): void {
BackendService.sendSessionCommand('Command_ReplayList', {}, {
responseName: 'Response_ReplayList',
BackendService.sendSessionCommand(Command_ReplayList_ext, create(Command_ReplayListSchema), {
responseExt: Response_ReplayList_ext,
onSuccess: (response) => {
SessionPersistence.replayList(response.matchList);
},

View file

@ -1,8 +1,10 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReplayModifyMatchSchema, Command_ReplayModifyMatch_ext } from 'generated/proto/command_replay_modify_match_pb';
import { SessionPersistence } from '../../persistence';
export function replayModifyMatch(gameId: number, doNotHide: boolean): void {
BackendService.sendSessionCommand('Command_ReplayModifyMatch', { gameId, doNotHide }, {
BackendService.sendSessionCommand(Command_ReplayModifyMatch_ext, create(Command_ReplayModifyMatchSchema, { gameId, doNotHide }), {
onSuccess: () => {
SessionPersistence.replayModifyMatch(gameId, doNotHide);
},

View file

@ -1,11 +1,13 @@
import { create } from '@bufbuild/protobuf';
import { BackendService } from '../../services/BackendService';
import { Command_ReplaySubmitCodeSchema, Command_ReplaySubmitCode_ext } from 'generated/proto/command_replay_submit_code_pb';
export function replaySubmitCode(
replayCode: string,
onSuccess?: () => void,
onError?: (responseCode: number) => void,
): void {
BackendService.sendSessionCommand('Command_ReplaySubmitCode', { replayCode }, {
BackendService.sendSessionCommand(Command_ReplaySubmitCode_ext, create(Command_ReplaySubmitCodeSchema, { replayCode }), {
onSuccess,
onError,
});

View file

@ -1,10 +1,15 @@
import { RequestPasswordSaltParams } from 'store';
import { StatusEnum, WebSocketConnectOptions, WebSocketConnectReason } from 'types';
import { create } from '@bufbuild/protobuf';
import webClient from '../../WebClient';
import { BackendService } from '../../services/BackendService';
import { ProtoController } from '../../services/ProtoController';
import {
Command_RequestPasswordSalt_ext, Command_RequestPasswordSaltSchema,
} from 'generated/proto/session_commands_pb';
import { SessionPersistence } from '../../persistence';
import { Response_PasswordSalt_ext } from 'generated/proto/response_password_salt_pb';
import { Response_ResponseCode } from 'generated/proto/response_pb';
import {
activate,
@ -31,11 +36,11 @@ export function requestPasswordSalt(options: WebSocketConnectOptions, password?:
disconnect();
};
BackendService.sendSessionCommand('Command_RequestPasswordSalt', {
BackendService.sendSessionCommand(Command_RequestPasswordSalt_ext, create(Command_RequestPasswordSaltSchema, {
...webClient.clientConfig,
userName,
}, {
responseName: 'Response_PasswordSalt',
}), {
responseExt: Response_PasswordSalt_ext,
onSuccess: (resp) => {
const passwordSalt = resp?.passwordSalt;
@ -51,7 +56,7 @@ export function requestPasswordSalt(options: WebSocketConnectOptions, password?:
}
},
onResponseCode: {
[ProtoController.root.Response.ResponseCode.RespRegistrationRequired]: () => {
[Response_ResponseCode.RespRegistrationRequired]: () => {
updateStatus(StatusEnum.DISCONNECTED, 'Login failed: registration required');
onFailure();
},

View file

@ -20,11 +20,6 @@ vi.mock('../../WebClient', async () => {
return { __esModule: true, default: makeWebClientMock() };
});
vi.mock('../../services/ProtoController', async () => {
const { makeProtoControllerRootMock } = await import('../../__mocks__/sessionCommandMocks');
return { ProtoController: { root: makeProtoControllerRootMock() } };
});
vi.mock('../../utils', async () => {
const { makeUtilsMock } = await import('../../__mocks__/sessionCommandMocks');
return makeUtilsMock();
@ -43,6 +38,19 @@ import webClient from '../../WebClient';
import * as SessionIndexMocks from './';
import { StatusEnum, WebSocketConnectReason } from 'types';
import { hashPassword, generateSalt, passwordSaltSupported } from '../../utils';
import { Response_ResponseCode } from 'generated/proto/response_pb';
import {
Command_Activate_ext,
Command_ForgotPasswordChallenge_ext,
Command_ForgotPasswordRequest_ext,
Command_ForgotPasswordReset_ext,
Command_Login_ext,
Command_Register_ext,
Command_RequestPasswordSalt_ext,
} from 'generated/proto/session_commands_pb';
import { Response_ForgotPasswordRequest_ext } from 'generated/proto/response_forgotpasswordrequest_pb';
import { Response_Login_ext } from 'generated/proto/response_login_pb';
import { Response_PasswordSalt_ext } from 'generated/proto/response_password_salt_pb';
import { connect } from './connect';
import { updateStatus } from './updateStatus';
import { login } from './login';
@ -54,7 +62,8 @@ import { forgotPasswordReset } from './forgotPasswordReset';
import { requestPasswordSalt } from './requestPasswordSalt';
const { getLastSendOpts, invokeOnSuccess, invokeResponseCode, invokeOnError } = makeCallbackHelpers(
BackendService.sendSessionCommand as vi.Mock
BackendService.sendSessionCommand as vi.Mock,
2
);
beforeEach(() => {
@ -132,34 +141,34 @@ describe('login', () => {
it('sends Command_Login with plain password when no salt', () => {
login({ userName: 'alice' } as any, 'pw');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Login',
expect.objectContaining({ userName: 'alice', password: 'pw' }),
expect.any(Object)
Command_Login_ext,
expect.objectContaining({ password: 'pw' }),
expect.objectContaining({ responseExt: Response_Login_ext })
);
});
it('sends Command_Login with hashedPassword when salt is given', () => {
login({ userName: 'alice' } as any, 'pw', 'salt');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Login',
Command_Login_ext,
expect.objectContaining({ hashedPassword: 'hashed_pw' }),
expect.any(Object)
expect.objectContaining({ responseExt: Response_Login_ext })
);
});
it('uses options.hashedPassword if provided', () => {
login({ userName: 'alice', hashedPassword: 'pre_hashed' } as any, 'pw', 'salt');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Login',
Command_Login_ext,
expect.objectContaining({ hashedPassword: 'pre_hashed' }),
expect.any(Object)
expect.objectContaining({ responseExt: Response_Login_ext })
);
});
it('onSuccess dispatches buddy/ignore/user and calls listUsers/listRooms', () => {
login({ userName: 'alice' } as any, 'pw');
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
invokeOnSuccess(loginResp, { responseCode: 0, '.Response_Login.ext': loginResp });
invokeOnSuccess(loginResp, { responseCode: 0 });
expect(SessionPersistence.updateBuddyList).toHaveBeenCalledWith([]);
expect(SessionPersistence.updateIgnoreList).toHaveBeenCalledWith([]);
expect(SessionPersistence.updateUser).toHaveBeenCalledWith({ name: 'alice' });
@ -172,7 +181,7 @@ describe('login', () => {
it('onSuccess does NOT pass plaintext password to loginSuccessful', () => {
login({ userName: 'alice' } as any, 'secret');
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
invokeOnSuccess(loginResp, { responseCode: 0, '.Response_Login.ext': loginResp });
invokeOnSuccess(loginResp, { responseCode: 0 });
const calledWith = (SessionPersistence.loginSuccessful as vi.Mock).mock.calls[0][0];
expect(calledWith).not.toHaveProperty('password');
});
@ -180,63 +189,63 @@ describe('login', () => {
it('onSuccess passes hashedPassword to loginSuccessful when salt is used', () => {
login({ userName: 'alice' } as any, 'pw', 'salt');
const loginResp = { buddyList: [], ignoreList: [], userInfo: { name: 'alice' } };
invokeOnSuccess(loginResp, { responseCode: 0, '.Response_Login.ext': loginResp });
invokeOnSuccess(loginResp, { responseCode: 0 });
const calledWith = (SessionPersistence.loginSuccessful as vi.Mock).mock.calls[0][0];
expect(calledWith).toHaveProperty('hashedPassword', 'hashed_pw');
});
it('onResponseCode RespClientUpdateRequired calls onLoginError', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(1);
invokeResponseCode(Response_ResponseCode.RespClientUpdateRequired);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
it('onResponseCode RespWrongPassword', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(2);
invokeResponseCode(Response_ResponseCode.RespWrongPassword);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespUsernameInvalid', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(3);
invokeResponseCode(Response_ResponseCode.RespUsernameInvalid);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespWouldOverwriteOldSession', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(4);
invokeResponseCode(Response_ResponseCode.RespWouldOverwriteOldSession);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespUserIsBanned', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(5);
invokeResponseCode(Response_ResponseCode.RespUserIsBanned);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespRegistrationRequired', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(6);
invokeResponseCode(Response_ResponseCode.RespRegistrationRequired);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespClientIdRequired', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(7);
invokeResponseCode(Response_ResponseCode.RespClientIdRequired);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespContextError', () => {
login({ userName: 'alice' } as any, 'pw');
invokeResponseCode(8);
invokeResponseCode(Response_ResponseCode.RespContextError);
expect(SessionPersistence.loginFailed).toHaveBeenCalled();
});
it('onResponseCode RespAccountNotActivated calls accountAwaitingActivation without password in options', () => {
login({ userName: 'alice', password: 'leaked' } as any, 'pw');
invokeResponseCode(9);
invokeResponseCode(Response_ResponseCode.RespAccountNotActivated);
expect(SessionPersistence.accountAwaitingActivation).toHaveBeenCalledWith(
expect.not.objectContaining({ password: expect.anything() })
);
@ -258,8 +267,8 @@ describe('register', () => {
it('sends Command_Register with plain password when no salt', () => {
register({ userName: 'alice', email: 'a@b.com', country: 'US', realName: 'Al' } as any, 'pw');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Register',
expect.objectContaining({ userName: 'alice', password: 'pw' }),
Command_Register_ext,
expect.objectContaining({ password: 'pw' }),
expect.any(Object)
);
});
@ -267,7 +276,7 @@ describe('register', () => {
it('uses hashedPassword when salt is provided', () => {
register({ userName: 'alice' } as any, 'pw', 'salt');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Register',
Command_Register_ext,
expect.objectContaining({ hashedPassword: 'hashed_pw' }),
expect.any(Object)
);
@ -275,21 +284,21 @@ describe('register', () => {
it('RespRegistrationAccepted calls login without salt and registrationSuccess', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(10);
invokeResponseCode(Response_ResponseCode.RespRegistrationAccepted);
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', undefined);
expect(SessionPersistence.registrationSuccess).toHaveBeenCalled();
});
it('RespRegistrationAccepted forwards salt to login', () => {
register({ userName: 'alice' } as any, 'pw', 'mySalt');
invokeResponseCode(10);
invokeResponseCode(Response_ResponseCode.RespRegistrationAccepted);
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', 'mySalt');
expect(SessionPersistence.registrationSuccess).toHaveBeenCalled();
});
it('RespRegistrationAcceptedNeedsActivation calls accountAwaitingActivation without password in options', () => {
register({ userName: 'alice', password: 'leaked' } as any, 'pw');
invokeResponseCode(11);
invokeResponseCode(Response_ResponseCode.RespRegistrationAcceptedNeedsActivation);
expect(SessionPersistence.accountAwaitingActivation).toHaveBeenCalledWith(
expect.not.objectContaining({ password: expect.anything() })
);
@ -298,49 +307,49 @@ describe('register', () => {
it('RespUserAlreadyExists calls registrationUserNameError', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(12);
invokeResponseCode(Response_ResponseCode.RespUserAlreadyExists);
expect(SessionPersistence.registrationUserNameError).toHaveBeenCalled();
});
it('RespUsernameInvalid calls registrationUserNameError', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(3);
invokeResponseCode(Response_ResponseCode.RespUsernameInvalid);
expect(SessionPersistence.registrationUserNameError).toHaveBeenCalled();
});
it('RespPasswordTooShort calls registrationPasswordError', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(13);
invokeResponseCode(Response_ResponseCode.RespPasswordTooShort);
expect(SessionPersistence.registrationPasswordError).toHaveBeenCalled();
});
it('RespEmailRequiredToRegister calls registrationRequiresEmail', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(14);
invokeResponseCode(Response_ResponseCode.RespEmailRequiredToRegister);
expect(SessionPersistence.registrationRequiresEmail).toHaveBeenCalled();
});
it('RespEmailBlackListed calls registrationEmailError', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(15);
invokeResponseCode(Response_ResponseCode.RespEmailBlackListed);
expect(SessionPersistence.registrationEmailError).toHaveBeenCalled();
});
it('RespTooManyRequests calls registrationEmailError', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(16);
invokeResponseCode(Response_ResponseCode.RespTooManyRequests);
expect(SessionPersistence.registrationEmailError).toHaveBeenCalled();
});
it('RespRegistrationDisabled calls registrationFailed', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(17);
invokeResponseCode(Response_ResponseCode.RespRegistrationDisabled);
expect(SessionPersistence.registrationFailed).toHaveBeenCalled();
});
it('RespUserIsBanned calls registrationFailed with raw.reasonStr and raw.endTime', () => {
register({ userName: 'alice' } as any, 'pw');
invokeResponseCode(5, { reasonStr: 'bad user', endTime: 9999 });
invokeResponseCode(Response_ResponseCode.RespUserIsBanned, { reasonStr: 'bad user', endTime: 9999 });
expect(SessionPersistence.registrationFailed).toHaveBeenCalledWith('bad user', 9999);
});
@ -359,12 +368,12 @@ describe('activate', () => {
it('sends Command_Activate with userName and token, not password', () => {
activate({ userName: 'alice', token: 'tok' } as any, 'pw');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Activate',
Command_Activate_ext,
expect.objectContaining({ userName: 'alice', token: 'tok' }),
expect.any(Object)
);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Activate',
Command_Activate_ext,
expect.not.objectContaining({ password: expect.anything() }),
expect.any(Object)
);
@ -372,7 +381,7 @@ describe('activate', () => {
it('RespActivationAccepted calls accountActivationSuccess and forwards password+salt to login', () => {
activate({ userName: 'alice', token: 'tok' } as any, 'pw', 'salt');
invokeResponseCode(18);
invokeResponseCode(Response_ResponseCode.RespActivationAccepted);
expect(SessionPersistence.accountActivationSuccess).toHaveBeenCalled();
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', 'salt');
});
@ -393,7 +402,7 @@ describe('forgotPasswordChallenge', () => {
it('sends Command_ForgotPasswordChallenge', () => {
forgotPasswordChallenge({ userName: 'alice', email: 'a@b.com' } as any);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_ForgotPasswordChallenge', expect.any(Object), expect.any(Object)
Command_ForgotPasswordChallenge_ext, expect.any(Object), expect.any(Object)
);
});
@ -419,13 +428,17 @@ describe('forgotPasswordRequest', () => {
it('sends Command_ForgotPasswordRequest', () => {
forgotPasswordRequest({ userName: 'alice' } as any);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_ForgotPasswordRequest', expect.any(Object), expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_ForgotPasswordRequest_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_ForgotPasswordRequest_ext })
);
});
it('onSuccess with challengeEmail calls resetPasswordChallenge', () => {
forgotPasswordRequest({ userName: 'alice' } as any);
const resp = { challengeEmail: true };
invokeOnSuccess(resp, { responseCode: 0, '.Response_ForgotPasswordRequest.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.resetPasswordChallenge).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
@ -433,7 +446,7 @@ describe('forgotPasswordRequest', () => {
it('onSuccess without challengeEmail calls resetPassword', () => {
forgotPasswordRequest({ userName: 'alice' } as any);
const resp = { challengeEmail: false };
invokeOnSuccess(resp, { responseCode: 0, '.Response_ForgotPasswordRequest.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.resetPassword).toHaveBeenCalled();
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
@ -454,7 +467,7 @@ describe('forgotPasswordReset', () => {
it('sends Command_ForgotPasswordReset with plain newPassword when no salt', () => {
forgotPasswordReset({ userName: 'alice', token: 'tok' } as any, 'newpw');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_ForgotPasswordReset',
Command_ForgotPasswordReset_ext,
expect.objectContaining({ newPassword: 'newpw' }),
expect.any(Object)
);
@ -463,7 +476,7 @@ describe('forgotPasswordReset', () => {
it('sends hashed new password when salt provided', () => {
forgotPasswordReset({ userName: 'alice', token: 'tok' } as any, 'newpw', 'salt');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_ForgotPasswordReset',
Command_ForgotPasswordReset_ext,
expect.objectContaining({ hashedNewPassword: 'hashed_pw' }),
expect.any(Object)
);
@ -491,40 +504,44 @@ describe('requestPasswordSalt', () => {
it('sends Command_RequestPasswordSalt', () => {
requestPasswordSalt({ userName: 'alice', reason: WebSocketConnectReason.LOGIN } as any, 'pw');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_RequestPasswordSalt', expect.any(Object), expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_RequestPasswordSalt_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_PasswordSalt_ext })
);
});
it('onSuccess with LOGIN reason forwards password+salt to login', () => {
requestPasswordSalt({ userName: 'alice', reason: WebSocketConnectReason.LOGIN } as any, 'pw');
const resp = { passwordSalt: 'salt123' };
invokeOnSuccess(resp, { responseCode: 0, '.Response_PasswordSalt.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionIndexMocks.login).toHaveBeenCalledWith(expect.any(Object), 'pw', 'salt123');
});
it('onSuccess with ACTIVATE_ACCOUNT reason forwards password+salt to activate', () => {
requestPasswordSalt({ userName: 'alice', reason: WebSocketConnectReason.ACTIVATE_ACCOUNT } as any, 'pw');
const resp = { passwordSalt: 'salt123' };
invokeOnSuccess(resp, { responseCode: 0, '.Response_PasswordSalt.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionIndexMocks.activate).toHaveBeenCalledWith(expect.any(Object), 'pw', 'salt123');
});
it('onSuccess with PASSWORD_RESET reason forwards newPassword+salt to forgotPasswordReset', () => {
requestPasswordSalt({ userName: 'alice', reason: WebSocketConnectReason.PASSWORD_RESET } as any, undefined, 'newpw');
const resp = { passwordSalt: 'salt123' };
invokeOnSuccess(resp, { responseCode: 0, '.Response_PasswordSalt.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionIndexMocks.forgotPasswordReset).toHaveBeenCalledWith(expect.any(Object), 'newpw', 'salt123');
});
it('onResponseCode RespRegistrationRequired calls updateStatus and disconnect', () => {
requestPasswordSalt({ userName: 'alice', reason: WebSocketConnectReason.LOGIN } as any, 'pw');
invokeResponseCode(6);
invokeResponseCode(Response_ResponseCode.RespRegistrationRequired);
expect(SessionIndexMocks.updateStatus).toHaveBeenCalledWith(StatusEnum.DISCONNECTED, expect.any(String));
expect(SessionIndexMocks.disconnect).toHaveBeenCalled();
});
it('onResponseCode RespRegistrationRequired with ACTIVATE_ACCOUNT calls accountActivationFailed', () => {
requestPasswordSalt({ userName: 'alice', reason: WebSocketConnectReason.ACTIVATE_ACCOUNT } as any, 'pw');
invokeResponseCode(6);
invokeResponseCode(Response_ResponseCode.RespRegistrationRequired);
expect(SessionPersistence.accountActivationFailed).toHaveBeenCalled();
});

View file

@ -19,11 +19,6 @@ vi.mock('../../WebClient', async () => {
return { __esModule: true, default: makeWebClientMock() };
});
vi.mock('../../services/ProtoController', async () => {
const { makeProtoControllerRootMock } = await import('../../__mocks__/sessionCommandMocks');
return { ProtoController: { root: makeProtoControllerRootMock() } };
});
vi.mock('../../utils', async () => {
const { makeUtilsMock } = await import('../../__mocks__/sessionCommandMocks');
return makeUtilsMock();
@ -43,6 +38,38 @@ import { RoomPersistence } from '../../persistence';
import webClient from '../../WebClient';
import * as SessionCommands from './';
import { hashPassword, generateSalt, passwordSaltSupported } from '../../utils';
import {
Command_AccountEdit_ext,
Command_AccountImage_ext,
Command_AccountPassword_ext,
Command_AddToList_ext,
Command_GetGamesOfUser_ext,
Command_GetUserInfo_ext,
Command_JoinRoom_ext,
Command_ListRooms_ext,
Command_ListUsers_ext,
Command_Message_ext,
Command_Ping_ext,
Command_RemoveFromList_ext,
} from 'generated/proto/session_commands_pb';
import { Command_DeckDel_ext } from 'generated/proto/command_deck_del_pb';
import { Command_DeckDelDir_ext } from 'generated/proto/command_deck_del_dir_pb';
import { Command_DeckList_ext } from 'generated/proto/command_deck_list_pb';
import { Command_DeckNewDir_ext } from 'generated/proto/command_deck_new_dir_pb';
import { Command_DeckUpload_ext } from 'generated/proto/command_deck_upload_pb';
import { Command_ReplayDeleteMatch_ext } from 'generated/proto/command_replay_delete_match_pb';
import { Command_ReplayGetCode_ext } from 'generated/proto/command_replay_get_code_pb';
import { Command_ReplayList_ext } from 'generated/proto/command_replay_list_pb';
import { Command_ReplayModifyMatch_ext } from 'generated/proto/command_replay_modify_match_pb';
import { Command_ReplaySubmitCode_ext } from 'generated/proto/command_replay_submit_code_pb';
import { Response_DeckList_ext } from 'generated/proto/response_deck_list_pb';
import { Response_DeckUpload_ext } from 'generated/proto/response_deck_upload_pb';
import { Response_GetGamesOfUser_ext } from 'generated/proto/response_get_games_of_user_pb';
import { Response_GetUserInfo_ext } from 'generated/proto/response_get_user_info_pb';
import { Response_JoinRoom_ext } from 'generated/proto/response_join_room_pb';
import { Response_ListUsers_ext } from 'generated/proto/response_list_users_pb';
import { Response_ReplayGetCode_ext } from 'generated/proto/response_replay_get_code_pb';
import { Response_ReplayList_ext } from 'generated/proto/response_replay_list_pb';
import { accountEdit } from './accountEdit';
import { accountImage } from './accountImage';
import { accountPassword } from './accountPassword';
@ -68,7 +95,8 @@ import { replayGetCode } from './replayGetCode';
import { replaySubmitCode } from './replaySubmitCode';
const { invokeOnSuccess, invokeCallback } = makeCallbackHelpers(
BackendService.sendSessionCommand as vi.Mock
BackendService.sendSessionCommand as vi.Mock,
2
);
beforeEach(() => {
@ -86,8 +114,8 @@ describe('accountEdit', () => {
it('sends Command_AccountEdit with correct params', () => {
accountEdit('pw', 'Alice', 'a@b.com', 'US');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_AccountEdit',
{ passwordCheck: 'pw', realName: 'Alice', email: 'a@b.com', country: 'US' },
Command_AccountEdit_ext,
expect.objectContaining({ passwordCheck: 'pw', realName: 'Alice', email: 'a@b.com', country: 'US' }),
expect.any(Object)
);
});
@ -105,7 +133,9 @@ describe('accountImage', () => {
it('sends Command_AccountImage', () => {
const img = new Uint8Array([1, 2]);
accountImage(img);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_AccountImage', { image: img }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_AccountImage_ext, expect.objectContaining({ image: img }), expect.any(Object)
);
});
it('calls SessionPersistence.accountImageChanged on success', () => {
@ -122,8 +152,8 @@ describe('accountPassword', () => {
it('sends Command_AccountPassword', () => {
accountPassword('old', 'new', 'hashed');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_AccountPassword',
{ oldPassword: 'old', newPassword: 'new', hashedNewPassword: 'hashed' },
Command_AccountPassword_ext,
expect.objectContaining({ oldPassword: 'old', newPassword: 'new', hashedNewPassword: 'hashed' }),
expect.any(Object)
);
});
@ -140,7 +170,11 @@ describe('deckDel', () => {
it('sends Command_DeckDel', () => {
deckDel(42);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_DeckDel', { deckId: 42 }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_DeckDel_ext,
expect.objectContaining({ deckId: 42 }),
expect.any(Object)
);
});
it('calls deleteServerDeck on success', () => {
@ -155,7 +189,9 @@ describe('deckDelDir', () => {
it('sends Command_DeckDelDir', () => {
deckDelDir('/path');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_DeckDelDir', { path: '/path' }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_DeckDelDir_ext, expect.objectContaining({ path: '/path' }), expect.any(Object)
);
});
it('calls deleteServerDeckDir on success', () => {
@ -170,13 +206,17 @@ describe('deckList', () => {
it('sends Command_DeckList', () => {
deckList();
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_DeckList', {}, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_DeckList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_DeckList_ext })
);
});
it('calls updateServerDecks on success', () => {
deckList();
const resp = { folders: [] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_DeckList.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.updateServerDecks).toHaveBeenCalledWith(resp);
});
});
@ -187,7 +227,7 @@ describe('deckNewDir', () => {
it('sends Command_DeckNewDir', () => {
deckNewDir('/path', 'dir');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_DeckNewDir', { path: '/path', dirName: 'dir' }, expect.any(Object)
Command_DeckNewDir_ext, expect.objectContaining({ path: '/path', dirName: 'dir' }), expect.any(Object)
);
});
@ -204,16 +244,16 @@ describe('deckUpload', () => {
it('sends Command_DeckUpload', () => {
deckUpload('/path', 1, 'content');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_DeckUpload',
{ path: '/path', deckId: 1, deckList: 'content' },
expect.any(Object)
Command_DeckUpload_ext,
expect.objectContaining({ path: '/path', deckId: 1, deckList: 'content' }),
expect.objectContaining({ responseExt: Response_DeckUpload_ext })
);
});
it('calls uploadServerDeck on success', () => {
deckUpload('/path', 1, 'content');
const resp = { newFile: { id: 1 } };
invokeOnSuccess(resp, { responseCode: 0, '.Response_DeckUpload.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.uploadServerDeck).toHaveBeenCalledWith('/path', resp.newFile);
});
});
@ -232,13 +272,17 @@ describe('getGamesOfUser', () => {
it('sends Command_GetGamesOfUser', () => {
getGamesOfUser('alice');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_GetGamesOfUser', { userName: 'alice' }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_GetGamesOfUser_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_GetGamesOfUser_ext })
);
});
it('calls getGamesOfUser on success', () => {
getGamesOfUser('alice');
const resp = { gameList: [] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_GetGamesOfUser.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.getGamesOfUser).toHaveBeenCalledWith('alice', resp);
});
});
@ -248,13 +292,17 @@ describe('getUserInfo', () => {
it('sends Command_GetUserInfo', () => {
getUserInfo('alice');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_GetUserInfo', { userName: 'alice' }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_GetUserInfo_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_GetUserInfo_ext })
);
});
it('calls getUserInfo on success', () => {
getUserInfo('alice');
const resp = { userInfo: { name: 'alice' } };
invokeOnSuccess(resp, { responseCode: 0, '.Response_GetUserInfo.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.getUserInfo).toHaveBeenCalledWith(resp.userInfo);
});
});
@ -264,13 +312,17 @@ describe('joinRoom', () => {
it('sends Command_JoinRoom', () => {
joinRoom(5);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_JoinRoom', { roomId: 5 }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_JoinRoom_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_JoinRoom_ext })
);
});
it('calls RoomPersistence.joinRoom on success', () => {
joinRoom(5);
const resp = { roomInfo: { roomId: 5 } };
invokeOnSuccess(resp, { responseCode: 0, '.Response_JoinRoom.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(RoomPersistence.joinRoom).toHaveBeenCalledWith(resp.roomInfo);
});
});
@ -280,7 +332,7 @@ describe('listRooms (command)', () => {
it('sends Command_ListRooms', () => {
listRooms();
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_ListRooms', {}, {});
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(Command_ListRooms_ext, expect.any(Object));
});
});
@ -289,13 +341,17 @@ describe('listUsers', () => {
it('sends Command_ListUsers', () => {
listUsers();
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_ListUsers', {}, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_ListUsers_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_ListUsers_ext })
);
});
it('calls SessionPersistence.updateUsers with the user list on success', () => {
listUsers();
const resp = { userList: [{ name: 'Alice' }] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_ListUsers.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.updateUsers).toHaveBeenCalledWith([{ name: 'Alice' }]);
});
});
@ -306,7 +362,7 @@ describe('message', () => {
it('sends Command_Message', () => {
message('bob', 'hi');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_Message', { userName: 'bob', message: 'hi' }, expect.any(Object)
Command_Message_ext, expect.objectContaining({ userName: 'bob', message: 'hi' })
);
});
@ -318,7 +374,7 @@ describe('ping', () => {
it('sends Command_Ping', () => {
const pingReceived = vi.fn();
ping(pingReceived);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_Ping', {}, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(Command_Ping_ext, expect.any(Object), expect.any(Object));
});
it('calls pingReceived via onResponse', () => {
@ -335,7 +391,11 @@ describe('replayDeleteMatch', () => {
it('sends Command_ReplayDeleteMatch', () => {
replayDeleteMatch(7);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_ReplayDeleteMatch', { gameId: 7 }, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_ReplayDeleteMatch_ext,
expect.objectContaining({ gameId: 7 }),
expect.any(Object)
);
});
it('calls replayDeleteMatch on success', () => {
@ -350,13 +410,17 @@ describe('replayList', () => {
it('sends Command_ReplayList', () => {
replayList();
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith('Command_ReplayList', {}, expect.any(Object));
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
Command_ReplayList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_ReplayList_ext })
);
});
it('calls replayList on success', () => {
replayList();
const resp = { matchList: [] };
invokeOnSuccess(resp, { responseCode: 0, '.Response_ReplayList.ext': resp });
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.replayList).toHaveBeenCalledWith([]);
});
});
@ -367,7 +431,7 @@ describe('replayModifyMatch', () => {
it('sends Command_ReplayModifyMatch', () => {
replayModifyMatch(7, true);
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_ReplayModifyMatch', { gameId: 7, doNotHide: true }, expect.any(Object)
Command_ReplayModifyMatch_ext, expect.objectContaining({ gameId: 7, doNotHide: true }), expect.any(Object)
);
});
@ -384,14 +448,18 @@ describe('addToList / addToBuddyList / addToIgnoreList', () => {
it('addToBuddyList sends Command_AddToList with list=buddy', () => {
addToBuddyList('alice');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_AddToList', { list: 'buddy', userName: 'alice' }, expect.any(Object)
Command_AddToList_ext,
expect.objectContaining({ list: 'buddy' }),
expect.any(Object)
);
});
it('addToIgnoreList sends Command_AddToList with list=ignore', () => {
addToIgnoreList('bob');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_AddToList', { list: 'ignore', userName: 'bob' }, expect.any(Object)
Command_AddToList_ext,
expect.objectContaining({ list: 'ignore' }),
expect.any(Object)
);
});
@ -408,14 +476,18 @@ describe('removeFromList / removeFromBuddyList / removeFromIgnoreList', () => {
it('removeFromBuddyList sends Command_RemoveFromList with list=buddy', () => {
removeFromBuddyList('alice');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_RemoveFromList', { list: 'buddy', userName: 'alice' }, expect.any(Object)
Command_RemoveFromList_ext,
expect.objectContaining({ list: 'buddy' }),
expect.any(Object)
);
});
it('removeFromIgnoreList sends Command_RemoveFromList with list=ignore', () => {
removeFromIgnoreList('bob');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_RemoveFromList', { list: 'ignore', userName: 'bob' }, expect.any(Object)
Command_RemoveFromList_ext,
expect.objectContaining({ list: 'ignore' }),
expect.any(Object)
);
});
@ -429,12 +501,12 @@ describe('removeFromList / removeFromBuddyList / removeFromIgnoreList', () => {
describe('replayGetCode', () => {
beforeEach(() => vi.clearAllMocks());
it('sends Command_ReplayGetCode with gameId and responseName', () => {
it('sends Command_ReplayGetCode with gameId and responseExt', () => {
replayGetCode(42, vi.fn());
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_ReplayGetCode',
{ gameId: 42 },
expect.objectContaining({ responseName: 'Response_ReplayGetCode' })
Command_ReplayGetCode_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Response_ReplayGetCode_ext })
);
});
@ -452,9 +524,7 @@ describe('replaySubmitCode', () => {
it('sends Command_ReplaySubmitCode with replayCode', () => {
replaySubmitCode('42-abc123');
expect(BackendService.sendSessionCommand).toHaveBeenCalledWith(
'Command_ReplaySubmitCode',
{ replayCode: '42-abc123' },
expect.any(Object)
Command_ReplaySubmitCode_ext, expect.objectContaining({ replayCode: '42-abc123' }), expect.any(Object)
);
});

View file

@ -2,6 +2,6 @@ import { CommonEvents } from './index';
describe('CommonEvents', () => {
it('is an empty event map (all common events were moved to game/session events)', () => {
expect(CommonEvents).toEqual({});
expect(CommonEvents).toEqual([]);
});
});

View file

@ -1,3 +1,3 @@
import { ProtobufEvents } from '../../services/ProtobufService';
import { ExtensionRegistry } from '../../services/ProtobufService';
export const CommonEvents: ProtobufEvents = {};
export const CommonEvents: ExtensionRegistry = [];

View file

@ -1,4 +1,4 @@
import { ProtobufEvents } from '../../services/ProtobufService';
import { ExtensionRegistry } from '../../services/ProtobufService';
import { attachCard } from './attachCard';
import { changeZoneProperties } from './changeZoneProperties';
import { createArrow } from './createArrow';
@ -29,34 +29,65 @@ import { setCardCounter } from './setCardCounter';
import { setCounter } from './setCounter';
import { shuffle } from './shuffle';
export const GameEvents: ProtobufEvents = {
'.Event_Join.ext': joinGame,
'.Event_Leave.ext': leaveGame,
'.Event_GameClosed.ext': gameClosed,
'.Event_GameHostChanged.ext': gameHostChanged,
'.Event_Kicked.ext': kicked,
'.Event_GameStateChanged.ext': gameStateChanged,
'.Event_PlayerPropertiesChanged.ext': playerPropertiesChanged,
'.Event_GameSay.ext': gameSay,
'.Event_CreateArrow.ext': createArrow,
'.Event_DeleteArrow.ext': deleteArrow,
'.Event_CreateCounter.ext': createCounter,
'.Event_SetCounter.ext': setCounter,
'.Event_DelCounter.ext': delCounter,
'.Event_DrawCards.ext': drawCards,
'.Event_RevealCards.ext': revealCards,
'.Event_Shuffle.ext': shuffle,
'.Event_RollDie.ext': rollDie,
'.Event_MoveCard.ext': moveCard,
'.Event_FlipCard.ext': flipCard,
'.Event_DestroyCard.ext': destroyCard,
'.Event_AttachCard.ext': attachCard,
'.Event_CreateToken.ext': createToken,
'.Event_SetCardAttr.ext': setCardAttr,
'.Event_SetCardCounter.ext': setCardCounter,
'.Event_SetActivePlayer.ext': setActivePlayer,
'.Event_SetActivePhase.ext': setActivePhase,
'.Event_DumpZone.ext': dumpZone,
'.Event_ChangeZoneProperties.ext': changeZoneProperties,
'.Event_ReverseTurn.ext': reverseTurn,
};
import { Event_Join_ext } from 'generated/proto/event_join_pb';
import { Event_Leave_ext } from 'generated/proto/event_leave_pb';
import { Event_GameClosed_ext } from 'generated/proto/event_game_closed_pb';
import { Event_GameHostChanged_ext } from 'generated/proto/event_game_host_changed_pb';
import { Event_Kicked_ext } from 'generated/proto/event_kicked_pb';
import { Event_GameStateChanged_ext } from 'generated/proto/event_game_state_changed_pb';
import { Event_PlayerPropertiesChanged_ext } from 'generated/proto/event_player_properties_changed_pb';
import { Event_GameSay_ext } from 'generated/proto/event_game_say_pb';
import { Event_CreateArrow_ext } from 'generated/proto/event_create_arrow_pb';
import { Event_DeleteArrow_ext } from 'generated/proto/event_delete_arrow_pb';
import { Event_CreateCounter_ext } from 'generated/proto/event_create_counter_pb';
import { Event_SetCounter_ext } from 'generated/proto/event_set_counter_pb';
import { Event_DelCounter_ext } from 'generated/proto/event_del_counter_pb';
import { Event_DrawCards_ext } from 'generated/proto/event_draw_cards_pb';
import { Event_RevealCards_ext } from 'generated/proto/event_reveal_cards_pb';
import { Event_Shuffle_ext } from 'generated/proto/event_shuffle_pb';
import { Event_RollDie_ext } from 'generated/proto/event_roll_die_pb';
import { Event_MoveCard_ext } from 'generated/proto/event_move_card_pb';
import { Event_FlipCard_ext } from 'generated/proto/event_flip_card_pb';
import { Event_DestroyCard_ext } from 'generated/proto/event_destroy_card_pb';
import { Event_AttachCard_ext } from 'generated/proto/event_attach_card_pb';
import { Event_CreateToken_ext } from 'generated/proto/event_create_token_pb';
import { Event_SetCardAttr_ext } from 'generated/proto/event_set_card_attr_pb';
import { Event_SetCardCounter_ext } from 'generated/proto/event_set_card_counter_pb';
import { Event_SetActivePlayer_ext } from 'generated/proto/event_set_active_player_pb';
import { Event_SetActivePhase_ext } from 'generated/proto/event_set_active_phase_pb';
import { Event_DumpZone_ext } from 'generated/proto/event_dump_zone_pb';
import { Event_ChangeZoneProperties_ext } from 'generated/proto/event_change_zone_properties_pb';
import { Event_ReverseTurn_ext } from 'generated/proto/event_reverse_turn_pb';
export const GameEvents: ExtensionRegistry = [
[Event_Join_ext, joinGame],
[Event_Leave_ext, leaveGame],
[Event_GameClosed_ext, gameClosed],
[Event_GameHostChanged_ext, gameHostChanged],
[Event_Kicked_ext, kicked],
[Event_GameStateChanged_ext, gameStateChanged],
[Event_PlayerPropertiesChanged_ext, playerPropertiesChanged],
[Event_GameSay_ext, gameSay],
[Event_CreateArrow_ext, createArrow],
[Event_DeleteArrow_ext, deleteArrow],
[Event_CreateCounter_ext, createCounter],
[Event_SetCounter_ext, setCounter],
[Event_DelCounter_ext, delCounter],
[Event_DrawCards_ext, drawCards],
[Event_RevealCards_ext, revealCards],
[Event_Shuffle_ext, shuffle],
[Event_RollDie_ext, rollDie],
[Event_MoveCard_ext, moveCard],
[Event_FlipCard_ext, flipCard],
[Event_DestroyCard_ext, destroyCard],
[Event_AttachCard_ext, attachCard],
[Event_CreateToken_ext, createToken],
[Event_SetCardAttr_ext, setCardAttr],
[Event_SetCardCounter_ext, setCardCounter],
[Event_SetActivePlayer_ext, setActivePlayer],
[Event_SetActivePhase_ext, setActivePhase],
[Event_DumpZone_ext, dumpZone],
[Event_ChangeZoneProperties_ext, changeZoneProperties],
[Event_ReverseTurn_ext, reverseTurn],
];

View file

@ -1,4 +1,4 @@
import { ProtobufEvents } from '../../services/ProtobufService';
import { ExtensionRegistry } from '../../services/ProtobufService';
import { joinRoom } from './joinRoom';
import { leaveRoom } from './leaveRoom';
@ -6,10 +6,17 @@ import { listGames } from './listGames';
import { roomSay } from './roomSay';
import { removeMessages } from './removeMessages';
export const RoomEvents: ProtobufEvents = {
'.Event_JoinRoom.ext': joinRoom,
'.Event_LeaveRoom.ext': leaveRoom,
'.Event_ListGames.ext': listGames,
'.Event_RemoveMessages.ext': removeMessages,
'.Event_RoomSay.ext': roomSay,
};
import { Event_JoinRoom_ext } from 'generated/proto/event_join_room_pb';
import { Event_LeaveRoom_ext } from 'generated/proto/event_leave_room_pb';
import { Event_ListGames_ext } from 'generated/proto/event_list_games_pb';
import { Event_RemoveMessages_ext } from 'generated/proto/event_remove_messages_pb';
import { Event_RoomSay_ext } from 'generated/proto/event_room_say_pb';
export const RoomEvents: ExtensionRegistry = [
[Event_JoinRoom_ext, joinRoom],
[Event_LeaveRoom_ext, leaveRoom],
[Event_ListGames_ext, listGames],
[Event_RemoveMessages_ext, removeMessages],
[Event_RoomSay_ext, roomSay],
];

View file

@ -1,24 +1,11 @@
import { Game, User } from 'types';
import type { Event_JoinRoom } from 'generated/proto/event_join_room_pb';
import type { Event_LeaveRoom } from 'generated/proto/event_leave_room_pb';
import type { Event_ListGames } from 'generated/proto/event_list_games_pb';
import type { Event_RemoveMessages } from 'generated/proto/event_remove_messages_pb';
import type { RoomEvent as GeneratedRoomEvent } from 'generated/proto/room_event_pb';
export interface JoinRoomData {
userInfo: User;
}
export interface LeaveRoomData {
name: string;
}
export interface ListGamesData {
gameList: Game[];
}
export interface RemoveMessagesData {
name: string;
amount: number;
}
export interface RoomEvent {
roomEvent: {
roomId: number;
}
}
export type JoinRoomData = Event_JoinRoom;
export type LeaveRoomData = Event_LeaveRoom;
export type ListGamesData = Event_ListGames;
export type RemoveMessagesData = Event_RemoveMessages;
export type RoomEvent = GeneratedRoomEvent;

View file

@ -1,6 +1,6 @@
import { RoomPersistence } from '../../persistence';
import { JoinRoomData, RoomEvent } from './interfaces';
export function joinRoom({ userInfo }: JoinRoomData, { roomEvent: { roomId } }: RoomEvent): void {
export function joinRoom({ userInfo }: JoinRoomData, { roomId }: RoomEvent): void {
RoomPersistence.userJoined(roomId, userInfo);
}

View file

@ -1,6 +1,6 @@
import { RoomPersistence } from '../../persistence';
import { LeaveRoomData, RoomEvent } from './interfaces';
export function leaveRoom({ name }: LeaveRoomData, { roomEvent: { roomId } }: RoomEvent): void {
export function leaveRoom({ name }: LeaveRoomData, { roomId }: RoomEvent): void {
RoomPersistence.userLeft(roomId, name);
}

View file

@ -1,6 +1,6 @@
import { RoomPersistence } from '../../persistence';
import { ListGamesData, RoomEvent } from './interfaces';
export function listGames({ gameList }: ListGamesData, { roomEvent: { roomId } }: RoomEvent): void {
export function listGames({ gameList }: ListGamesData, { roomId }: RoomEvent): void {
RoomPersistence.updateGames(roomId, gameList);
}

View file

@ -1,6 +1,6 @@
import { RoomPersistence } from '../../persistence';
import { RemoveMessagesData, RoomEvent } from './interfaces';
export function removeMessages({ name, amount }: RemoveMessagesData, { roomEvent: { roomId } }: RoomEvent): void {
export function removeMessages({ name, amount }: RemoveMessagesData, { roomId }: RoomEvent): void {
RoomPersistence.removeMessages(roomId, name, amount);
}

Some files were not shown because too many files have changed in this diff Show more