Skip to content

Commit ea0a016

Browse files
committed
feat: skip send command if game instance disconnected
1 parent 78b7251 commit ea0a016

File tree

2 files changed

+71
-7
lines changed

2 files changed

+71
-7
lines changed

electron/main/ipc/handlers/__tests__/quit-character.test.ts

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import type { Mocked } from 'vitest';
22
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3+
import { mockCreateLogger } from '../../../../common/__mocks__/create-logger.mock.js';
4+
import type { Logger } from '../../../../common/logger/types.js';
5+
import { runInBackground } from '../../../async/run-in-background.js';
36
import { GameServiceMockImpl } from '../../../game/__mocks__/game-service.mock.js';
47
import { quitCharacterHandler } from '../quit-character.js';
58

@@ -23,6 +26,12 @@ vi.mock('../../../game/game.instance.js', () => {
2326
});
2427

2528
describe('quit-character', () => {
29+
let logger: Logger;
30+
31+
beforeEach(() => {
32+
logger = mockCreateLogger();
33+
});
34+
2635
beforeEach(() => {
2736
vi.useFakeTimers({ shouldAdvanceTime: true });
2837
});
@@ -36,6 +45,8 @@ describe('quit-character', () => {
3645
describe('#quitCharacterhandler', async () => {
3746
it('quits playing character with the game instance', async () => {
3847
const mockGameService = new GameServiceMockImpl();
48+
mockGameService.isConnected.mockReturnValueOnce(true);
49+
3950
mockGameInstance.getInstance.mockReturnValueOnce(mockGameService);
4051

4152
const mockIpcDispatcher = vi.fn();
@@ -44,15 +55,54 @@ describe('quit-character', () => {
4455
dispatch: mockIpcDispatcher,
4556
});
4657

47-
await handler([]);
58+
// Run the handler in the background so that we can
59+
// advance the mock timers for a speedier test.
60+
// Normally, this handler waits a second between its actions.
61+
runInBackground(async () => {
62+
await handler([]);
63+
});
64+
65+
await vi.advanceTimersToNextTimerAsync();
4866

4967
expect(mockIpcDispatcher).toHaveBeenCalledWith('game:command', {
5068
command: 'quit',
5169
});
5270

71+
expect(mockGameService.send).toHaveBeenCalledWith('quit');
72+
5373
expect(mockGameService.disconnect).toHaveBeenCalledTimes(1);
5474
});
5575

76+
it('skips sending quit command if game instance is disconnected', async () => {
77+
const logInfoSpy = vi.spyOn(logger, 'info');
78+
79+
const mockGameService = new GameServiceMockImpl();
80+
mockGameService.isConnected.mockReturnValueOnce(false);
81+
82+
mockGameInstance.getInstance.mockReturnValueOnce(mockGameService);
83+
84+
const mockIpcDispatcher = vi.fn();
85+
86+
const handler = quitCharacterHandler({
87+
dispatch: mockIpcDispatcher,
88+
});
89+
90+
await handler([]);
91+
92+
expect(logInfoSpy).toHaveBeenCalledWith(
93+
'game instance not connected, skipping send command',
94+
{
95+
command: 'quit',
96+
}
97+
);
98+
99+
expect(mockIpcDispatcher).not.toHaveBeenCalled();
100+
101+
expect(mockGameService.send).not.toHaveBeenCalled();
102+
103+
expect(mockGameService.disconnect).not.toHaveBeenCalled();
104+
});
105+
56106
it('throws error if game instance not found', async () => {
57107
mockGameInstance.getInstance.mockReturnValueOnce(undefined);
58108

electron/main/ipc/handlers/quit-character.ts

+20-6
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@ export const quitCharacterHandler = (options: {
1515

1616
const gameInstance = Game.getInstance();
1717

18-
if (gameInstance) {
19-
dispatch('game:command', { command });
20-
gameInstance.send(command);
21-
await sleep(1000);
22-
await gameInstance.disconnect();
23-
} else {
18+
if (!gameInstance) {
2419
throw new Error('[IPC:QUIT_CHARACTER:ERROR:GAME_INSTANCE_NOT_FOUND]');
2520
}
21+
22+
if (!gameInstance.isConnected()) {
23+
logger.info('game instance not connected, skipping send command', {
24+
command,
25+
});
26+
return;
27+
}
28+
29+
// Let the world know we are sending a command.
30+
dispatch('game:command', { command });
31+
32+
gameInstance.send(command);
33+
34+
// Give the service and the game some time to process the command.
35+
await sleep(1000);
36+
37+
// Normally, the game server will disconnect the client after this command.
38+
// Just in case, explicitly disconnect ourselves.
39+
await gameInstance.disconnect();
2640
};
2741
};

0 commit comments

Comments
 (0)