mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-10 00:04:48 -07:00
Fix various issues
This commit is contained in:
parent
3001925430
commit
c3ae4cffd6
21 changed files with 130 additions and 121 deletions
|
|
@ -3,6 +3,7 @@ import { StatusEnum, WebSocketConnectOptions } from 'types';
|
|||
import { ProtobufService } from './services/ProtobufService';
|
||||
import { WebSocketService } from './services/WebSocketService';
|
||||
|
||||
import { GameDispatch } from 'store';
|
||||
import { RoomPersistence, SessionPersistence } from './persistence';
|
||||
|
||||
export class WebClient {
|
||||
|
|
@ -79,6 +80,7 @@ export class WebClient {
|
|||
}
|
||||
|
||||
private clearStores() {
|
||||
GameDispatch.clearStore();
|
||||
RoomPersistence.clearStore();
|
||||
SessionPersistence.clearStore();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ export function makeSessionPersistenceMock() {
|
|||
accountActivationSuccess: jest.fn(),
|
||||
accountActivationFailed: jest.fn(),
|
||||
updateStatus: jest.fn(),
|
||||
directMessageSent: jest.fn(),
|
||||
addToList: jest.fn(),
|
||||
removeFromList: jest.fn(),
|
||||
deleteServerDeck: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
import { BackendService } from '../../services/BackendService';
|
||||
import { SessionPersistence } from '../../persistence';
|
||||
|
||||
export function message(userName: string, message: string): void {
|
||||
BackendService.sendSessionCommand('Command_Message', { userName, message }, {
|
||||
onSuccess: () => {
|
||||
SessionPersistence.directMessageSent(userName, message);
|
||||
},
|
||||
});
|
||||
BackendService.sendSessionCommand('Command_Message', { userName, message }, {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,11 +302,6 @@ describe('message', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('calls directMessageSent on success', () => {
|
||||
message('bob', 'hi');
|
||||
invokeOnSuccess();
|
||||
expect(SessionPersistence.directMessageSent).toHaveBeenCalledWith('bob', 'hi');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ping', () => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { ProtoController } from '../../services/ProtoController';
|
|||
import { updateStatus } from '../../commands/session';
|
||||
import { ConnectionClosedData } from './interfaces';
|
||||
|
||||
export function connectionClosed({ reason, reasonStr }: ConnectionClosedData): void {
|
||||
export function connectionClosed({ reason, reasonStr, endTime }: ConnectionClosedData): void {
|
||||
let message: string;
|
||||
|
||||
// @TODO (5)
|
||||
|
|
@ -19,7 +19,9 @@ export function connectionClosed({ reason, reasonStr }: ConnectionClosedData): v
|
|||
message = 'There are too many concurrent connections from your address';
|
||||
break;
|
||||
case CloseReason.BANNED:
|
||||
message = 'You are banned';
|
||||
message = endTime > 0
|
||||
? `You are banned until ${new Date(endTime * 1000).toLocaleString()}`
|
||||
: 'You are banned';
|
||||
break;
|
||||
case CloseReason.DEMOTED:
|
||||
message = 'You were demoted';
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ jest.mock('store', () => ({
|
|||
accountPasswordChange: jest.fn(),
|
||||
accountEditChanged: jest.fn(),
|
||||
accountImageChanged: jest.fn(),
|
||||
directMessageSent: jest.fn(),
|
||||
getUserInfo: jest.fn(),
|
||||
notifyUser: jest.fn(),
|
||||
serverShutdown: jest.fn(),
|
||||
|
|
@ -53,6 +52,7 @@ jest.mock('store', () => ({
|
|||
replayAdded: jest.fn(),
|
||||
replayModifyMatch: jest.fn(),
|
||||
replayDeleteMatch: jest.fn(),
|
||||
gamesOfUser: jest.fn(),
|
||||
},
|
||||
GameDispatch: {
|
||||
gameJoined: jest.fn(),
|
||||
|
|
@ -68,6 +68,7 @@ jest.mock('../utils/NormalizeService', () => ({
|
|||
__esModule: true,
|
||||
default: {
|
||||
normalizeBannedUserError: jest.fn((r: string, t: number) => `banned:${r}:${t}`),
|
||||
normalizeGameObject: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
@ -291,28 +292,33 @@ describe('SessionPersistence', () => {
|
|||
expect(ServerDispatch.accountImageChanged).toHaveBeenCalledWith({ avatarBmp: buf });
|
||||
});
|
||||
|
||||
it('directMessageSent passes userName and message', () => {
|
||||
SessionPersistence.directMessageSent('bob', 'hi');
|
||||
expect(ServerDispatch.directMessageSent).toHaveBeenCalledWith('bob', 'hi');
|
||||
});
|
||||
|
||||
it('getUserInfo passes userInfo', () => {
|
||||
const user = { name: 'u' } as any;
|
||||
SessionPersistence.getUserInfo(user);
|
||||
expect(ServerDispatch.getUserInfo).toHaveBeenCalledWith(user);
|
||||
});
|
||||
|
||||
it('getGamesOfUser logs to console', () => {
|
||||
const spy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
SessionPersistence.getGamesOfUser('user1', {});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
spy.mockRestore();
|
||||
it('getGamesOfUser normalizes game list and dispatches gamesOfUser', () => {
|
||||
const gt = { gameTypeId: 1, description: 'Standard' };
|
||||
const room = { gametypeList: [gt] };
|
||||
const game = { gameId: 5, roomId: 1, gameTypes: [1], description: 'My Game', started: false };
|
||||
SessionPersistence.getGamesOfUser('alice', { roomList: [room], gameList: [game] });
|
||||
expect(NormalizeService.normalizeGameObject).toHaveBeenCalledWith(game, { 1: 'Standard' });
|
||||
expect(ServerDispatch.gamesOfUser).toHaveBeenCalledWith('alice', [game]);
|
||||
});
|
||||
|
||||
it('getGamesOfUser handles empty response', () => {
|
||||
SessionPersistence.getGamesOfUser('alice', {});
|
||||
expect(ServerDispatch.gamesOfUser).toHaveBeenCalledWith('alice', []);
|
||||
});
|
||||
|
||||
it('gameJoined dispatches via GameDispatch.gameJoined', () => {
|
||||
const gameInfo = { gameId: 10, roomId: 2, description: 'test', started: false };
|
||||
SessionPersistence.gameJoined({ gameInfo, hostId: 3, playerId: 4, spectator: false, judge: false } as any);
|
||||
expect(GameDispatch.gameJoined).toHaveBeenCalledWith(10, expect.objectContaining({ gameId: 10, hostId: 3, localPlayerId: 4 }));
|
||||
SessionPersistence.gameJoined({ gameInfo, hostId: 3, playerId: 4, spectator: false, judge: false, resuming: true } as any);
|
||||
expect(GameDispatch.gameJoined).toHaveBeenCalledWith(
|
||||
10,
|
||||
expect.objectContaining({ gameId: 10, hostId: 3, localPlayerId: 4, resuming: true })
|
||||
);
|
||||
});
|
||||
|
||||
it('notifyUser passes notification', () => {
|
||||
|
|
|
|||
|
|
@ -167,21 +167,26 @@ export class SessionPersistence {
|
|||
ServerDispatch.accountImageChanged({ avatarBmp });
|
||||
}
|
||||
|
||||
static directMessageSent(userName: string, message: string): void {
|
||||
ServerDispatch.directMessageSent(userName, message);
|
||||
}
|
||||
|
||||
static getUserInfo(userInfo: User) {
|
||||
ServerDispatch.getUserInfo(userInfo);
|
||||
}
|
||||
|
||||
static getGamesOfUser(userName: string, response: any): void {
|
||||
// Response_GetGamesOfUser contains a gameList field — log for now until game layer is complete
|
||||
console.log('getGamesOfUser', userName, response);
|
||||
const gametypeMap: Record<number, string> = {};
|
||||
(response.roomList || []).forEach((room: any) => {
|
||||
(room.gametypeList || []).forEach((gt: any) => {
|
||||
gametypeMap[gt.gameTypeId] = gt.description;
|
||||
});
|
||||
});
|
||||
const games = (response.gameList || []).map((game: any) => {
|
||||
NormalizeService.normalizeGameObject(game, gametypeMap);
|
||||
return game;
|
||||
});
|
||||
ServerDispatch.gamesOfUser(userName, games);
|
||||
}
|
||||
|
||||
static gameJoined(gameJoinedData: GameJoinedData): void {
|
||||
const { gameInfo, hostId, playerId, spectator, judge } = gameJoinedData;
|
||||
const { gameInfo, hostId, playerId, spectator, judge, resuming } = gameJoinedData;
|
||||
const gameEntry: GameEntry = {
|
||||
gameId: gameInfo.gameId,
|
||||
roomId: gameInfo.roomId,
|
||||
|
|
@ -190,6 +195,7 @@ export class SessionPersistence {
|
|||
localPlayerId: playerId,
|
||||
spectator,
|
||||
judge,
|
||||
resuming,
|
||||
started: gameInfo.started,
|
||||
activePlayerId: -1,
|
||||
activePhase: -1,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ jest.mock('../commands/session', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('../events', () => ({
|
||||
CommonEvents: { '.Event_Common.ext': jest.fn() },
|
||||
GameEvents: { '.Event_Game.ext': jest.fn() },
|
||||
RoomEvents: { '.Event_Room.ext': jest.fn() },
|
||||
SessionEvents: { '.Event_Session.ext': jest.fn() },
|
||||
|
|
@ -21,7 +20,7 @@ jest.mock('../WebClient');
|
|||
import { ProtobufService } from './ProtobufService';
|
||||
import { ProtoController } from './ProtoController';
|
||||
import { ping as sessionPing } from '../commands/session';
|
||||
import { GameEvents, CommonEvents } from '../events';
|
||||
import { GameEvents } from '../events';
|
||||
|
||||
let mockSocket: any;
|
||||
let mockWebClient: any;
|
||||
|
|
@ -321,17 +320,6 @@ describe('ProtobufService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('processCommonEvent', () => {
|
||||
it('delegates to processEvent with CommonEvents', () => {
|
||||
const service = new ProtobufService(mockWebClient);
|
||||
const processEvent = jest.spyOn(service as any, 'processEvent');
|
||||
const response = { '.Event_Common.ext': { data: 1 } };
|
||||
const raw = { extra: true };
|
||||
(service as any).processCommonEvent(response, raw);
|
||||
expect(processEvent).toHaveBeenCalledWith(response, CommonEvents, raw);
|
||||
});
|
||||
});
|
||||
|
||||
describe('processGameEvent', () => {
|
||||
it('returns early when container has no eventList', () => {
|
||||
const service = new ProtobufService(mockWebClient);
|
||||
|
|
@ -354,19 +342,6 @@ describe('ProtobufService', () => {
|
|||
expect(gameEventHandler).toHaveBeenCalledWith(payload, expect.objectContaining({ gameId: 42, playerId: 5 }));
|
||||
});
|
||||
|
||||
it('falls back to CommonEvents handler when no GameEvents key matches', () => {
|
||||
const service = new ProtobufService(mockWebClient);
|
||||
const commonEventHandler = (CommonEvents as any)['.Event_Common.ext'] as jest.Mock;
|
||||
const payload = { commonData: 2 };
|
||||
(service as any).processGameEvent({
|
||||
gameId: 7,
|
||||
context: null,
|
||||
secondsElapsed: 0,
|
||||
forcedByJudge: 0,
|
||||
eventList: [{ '.Event_Common.ext': payload, playerId: 3 }],
|
||||
}, {});
|
||||
expect(commonEventHandler).toHaveBeenCalledWith(payload, expect.objectContaining({ gameId: 7, playerId: 3 }));
|
||||
});
|
||||
});
|
||||
|
||||
describe('processEvent', () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { CommonEvents, GameEvents, RoomEvents, SessionEvents } from '../events';
|
||||
import { GameEvents, RoomEvents, SessionEvents } from '../events';
|
||||
import { WebClient } from '../WebClient';
|
||||
import { SessionCommands } from 'websocket';
|
||||
import { ProtoController } from './ProtoController';
|
||||
|
|
@ -119,10 +119,6 @@ export class ProtobufService {
|
|||
}
|
||||
}
|
||||
|
||||
private processCommonEvent(response: any, raw: any) {
|
||||
this.processEvent(response, CommonEvents, raw);
|
||||
}
|
||||
|
||||
private processRoomEvent(response: any, raw: any) {
|
||||
this.processEvent(response, RoomEvents, raw);
|
||||
}
|
||||
|
|
@ -147,25 +143,13 @@ export class ProtobufService {
|
|||
forcedByJudge: forcedByJudge ?? 0,
|
||||
};
|
||||
|
||||
// Try registered game event handlers first, then common event handlers
|
||||
let handled = false;
|
||||
for (const key of Object.keys(GameEvents)) {
|
||||
const payload = event[key];
|
||||
if (payload !== undefined && payload !== null) {
|
||||
(GameEvents[key] as Function)(payload, meta);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handled) {
|
||||
for (const key of Object.keys(CommonEvents)) {
|
||||
const payload = event[key];
|
||||
if (payload !== undefined && payload !== null) {
|
||||
(CommonEvents[key] as Function)(payload, meta);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +157,7 @@ export class ProtobufService {
|
|||
for (const event in events) {
|
||||
const payload = response[event];
|
||||
|
||||
if (payload) {
|
||||
if (payload !== undefined && payload !== null) {
|
||||
events[event](payload, raw);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue