refactor web socket layer

This commit is contained in:
seavor 2026-04-14 14:39:46 -05:00
parent 19f5eefdd2
commit 141f0e59f5
124 changed files with 927 additions and 853 deletions

View file

@ -1,4 +1,4 @@
import { BackendService } from '../../services/BackendService';
import webClient from '../../WebClient';
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';
@ -66,8 +66,9 @@ import { undoDraw } from './undoDraw';
import { unconcede } from './unconcede';
import { judge } from './judge';
vi.mock('../../services/BackendService', () => ({
BackendService: { sendGameCommand: vi.fn() },
vi.mock('../../WebClient', () => ({
__esModule: true,
default: { protobuf: { sendGameCommand: vi.fn() } },
}));
const gameId = 1;
@ -75,116 +76,124 @@ const gameId = 1;
import { Mock } from 'vitest';
beforeEach(() => {
(BackendService.sendGameCommand as Mock).mockClear();
(webClient.protobuf.sendGameCommand as Mock).mockClear();
});
describe('Game commands — delegate to BackendService.sendGameCommand', () => {
describe('Game commands — delegate to webClient.protobuf.sendGameCommand', () => {
it('attachCard sends Command_AttachCard', () => {
attachCard(gameId, { cardId: 10, startZone: 'hand' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_AttachCard_ext, expect.objectContaining({ cardId: 10, startZone: 'hand' })
);
});
it('changeZoneProperties sends Command_ChangeZoneProperties', () => {
changeZoneProperties(gameId, { zoneName: 'side' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.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_ext, expect.any(Object));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Concede_ext, expect.any(Object));
});
it('createArrow sends Command_CreateArrow', () => {
createArrow(gameId, { startPlayerId: 1, startZone: 'hand' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_CreateArrow_ext, expect.objectContaining({ startPlayerId: 1, startZone: 'hand' })
);
});
it('createCounter sends Command_CreateCounter', () => {
createCounter(gameId, { counterName: 'life' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_CreateCounter_ext, expect.objectContaining({ counterName: 'life' })
);
});
it('createToken sends Command_CreateToken', () => {
createToken(gameId, { cardName: 'Goblin', zone: 'play' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_CreateToken_ext, expect.objectContaining({ cardName: 'Goblin', zone: 'play' })
);
});
it('deckSelect sends Command_DeckSelect', () => {
deckSelect(gameId, { deckId: 5 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DeckSelect_ext, expect.objectContaining({ deckId: 5 }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DeckSelect_ext, expect.objectContaining({ deckId: 5 }));
});
it('delCounter sends Command_DelCounter', () => {
delCounter(gameId, { counterId: 3 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DelCounter_ext, expect.objectContaining({ counterId: 3 }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_DelCounter_ext, expect.objectContaining({ counterId: 3 })
);
});
it('deleteArrow sends Command_DeleteArrow', () => {
deleteArrow(gameId, { arrowId: 2 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DeleteArrow_ext, expect.objectContaining({ arrowId: 2 }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_DeleteArrow_ext, expect.objectContaining({ arrowId: 2 })
);
});
it('drawCards sends Command_DrawCards', () => {
drawCards(gameId, { number: 3 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DrawCards_ext, expect.objectContaining({ number: 3 }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_DrawCards_ext, expect.objectContaining({ number: 3 }));
});
it('dumpZone sends Command_DumpZone', () => {
dumpZone(gameId, { playerId: 2, zoneName: 'library' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_DumpZone_ext, expect.objectContaining({ playerId: 2, zoneName: 'library' })
);
});
it('flipCard sends Command_FlipCard', () => {
flipCard(gameId, { cardId: 7, faceDown: false });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_FlipCard_ext, expect.objectContaining({ cardId: 7, faceDown: false })
);
});
it('gameSay sends Command_GameSay', () => {
gameSay(gameId, { message: 'hello' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_GameSay_ext, expect.objectContaining({ message: 'hello' }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_GameSay_ext, expect.objectContaining({ message: 'hello' })
);
});
it('incCardCounter sends Command_IncCardCounter', () => {
incCardCounter(gameId, { cardId: 5, counterId: 1 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_IncCardCounter_ext, expect.objectContaining({ cardId: 5, counterId: 1 })
);
});
it('incCounter sends Command_IncCounter', () => {
incCounter(gameId, { counterId: 1, delta: 5 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_IncCounter_ext, expect.objectContaining({ counterId: 1, delta: 5 })
);
});
it('kickFromGame sends Command_KickFromGame', () => {
kickFromGame(gameId, { playerId: 2 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_KickFromGame_ext, expect.objectContaining({ playerId: 2 }));
expect(webClient.protobuf.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_ext, expect.any(Object));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_LeaveGame_ext, expect.any(Object));
});
it('moveCard sends Command_MoveCard', () => {
moveCard(gameId, { startZone: 'hand', targetZone: 'graveyard' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_MoveCard_ext,
expect.objectContaining({ startZone: 'hand', targetZone: 'graveyard' })
);
@ -192,39 +201,45 @@ describe('Game commands — delegate to BackendService.sendGameCommand', () => {
it('mulligan sends Command_Mulligan', () => {
mulligan(gameId, { number: 7 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Mulligan_ext, expect.objectContaining({ number: 7 }));
expect(webClient.protobuf.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_ext, expect.any(Object));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_NextTurn_ext, expect.any(Object));
});
it('readyStart sends Command_ReadyStart', () => {
readyStart(gameId, { ready: true });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_ReadyStart_ext, expect.objectContaining({ ready: true }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_ReadyStart_ext, expect.objectContaining({ ready: true })
);
});
it('revealCards sends Command_RevealCards', () => {
revealCards(gameId, { zoneName: 'hand', cardId: [1, 2] });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.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_ext, expect.any(Object));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_ReverseTurn_ext, expect.any(Object));
});
it('setActivePhase sends Command_SetActivePhase', () => {
setActivePhase(gameId, { phase: 2 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_SetActivePhase_ext, expect.objectContaining({ phase: 2 }));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetActivePhase_ext, expect.objectContaining({ phase: 2 })
);
});
it('setCardAttr sends Command_SetCardAttr', () => {
setCardAttr(gameId, { zone: 'play', cardId: 5, attrValue: '2' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetCardAttr_ext,
expect.objectContaining({ zone: 'play', cardId: 5, attrValue: '2' })
);
@ -232,45 +247,47 @@ describe('Game commands — delegate to BackendService.sendGameCommand', () => {
it('setCardCounter sends Command_SetCardCounter', () => {
setCardCounter(gameId, { cardId: 5, counterId: 1 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetCardCounter_ext, expect.objectContaining({ cardId: 5, counterId: 1 })
);
});
it('setCounter sends Command_SetCounter', () => {
setCounter(gameId, { counterId: 1, value: 10 });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetCounter_ext, expect.objectContaining({ counterId: 1, value: 10 })
);
});
it('setSideboardLock sends Command_SetSideboardLock', () => {
setSideboardLock(gameId, { locked: true });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetSideboardLock_ext, expect.objectContaining({ locked: true })
);
});
it('setSideboardPlan sends Command_SetSideboardPlan', () => {
setSideboardPlan(gameId, { moveList: [] });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId, Command_SetSideboardPlan_ext, expect.objectContaining({ moveList: expect.any(Array) })
);
});
it('shuffle sends Command_Shuffle', () => {
shuffle(gameId, { zoneName: 'hand' });
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Shuffle_ext, expect.objectContaining({ zoneName: 'hand' }));
expect(webClient.protobuf.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_ext, expect.any(Object));
expect(webClient.protobuf.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_ext, expect.any(Object));
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(gameId, Command_Unconcede_ext, expect.any(Object));
});
it('judge sends Command_Judge with targetId and wrapped gameCommand array', () => {
@ -278,7 +295,7 @@ describe('Game commands — delegate to BackendService.sendGameCommand', () => {
const innerCmd = create(GameCommandSchema);
setExtension(innerCmd, Command_DrawCards_ext, create(Command_DrawCardsSchema, { number: 2 }));
judge(gameId, targetId, innerCmd);
expect(BackendService.sendGameCommand).toHaveBeenCalledWith(
expect(webClient.protobuf.sendGameCommand).toHaveBeenCalledWith(
gameId,
Command_Judge_ext,
expect.objectContaining({ targetId: 3, gameCommand: expect.any(Array) })