refactor redux data model

This commit is contained in:
seavor 2026-04-15 21:48:03 -05:00
parent ae1bc3da38
commit 0ff391491d
243 changed files with 5212 additions and 5963 deletions

View file

@ -1,16 +1,8 @@
// Shared mock setup for session command tests
vi.mock('../../persistence', async () => {
const { makeSessionPersistenceMock } = await import('../../__mocks__/sessionCommandMocks');
return {
SessionPersistence: makeSessionPersistenceMock(),
RoomPersistence: { joinRoom: vi.fn() },
};
});
vi.mock('../../WebClient', async () => {
const { makeWebClientMock } = await import('../../__mocks__/sessionCommandMocks');
return { __esModule: true, default: makeWebClientMock() };
return { WebClient: { instance: makeWebClientMock() } };
});
vi.mock('../../utils', async () => {
@ -27,9 +19,7 @@ vi.mock('./', async () => {
import { Mock } from 'vitest';
import { makeCallbackHelpers } from '../../__mocks__/callbackHelpers';
import { SessionPersistence } from '../../persistence';
import { RoomPersistence } from '../../persistence';
import webClient from '../../WebClient';
import { WebClient } from '../../WebClient';
import { hashPassword, generateSalt, passwordSaltSupported } from '../../utils';
import { accountEdit } from './accountEdit';
@ -57,8 +47,9 @@ import { replayGetCode } from './replayGetCode';
import { replaySubmitCode } from './replaySubmitCode';
import { Data } from '@app/types';
const { invokeOnSuccess, invokeCallback } = makeCallbackHelpers(
webClient.protobuf.sendSessionCommand as Mock,
WebClient.instance.protobuf.sendSessionCommand as Mock,
2
);
@ -73,17 +64,17 @@ beforeEach(() => {
describe('accountEdit', () => {
it('sends Command_AccountEdit with correct params', () => {
accountEdit('pw', 'Alice', 'a@b.com', 'US');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AccountEdit_ext,
expect.objectContaining({ passwordCheck: 'pw', realName: 'Alice', email: 'a@b.com', country: 'US' }),
expect.any(Object)
);
});
it('calls SessionPersistence.accountEditChanged on success', () => {
it('calls WebClient.instance.response.session.accountEditChanged on success', () => {
accountEdit('pw', 'Alice', 'a@b.com', 'US');
invokeOnSuccess();
expect(SessionPersistence.accountEditChanged).toHaveBeenCalledWith('Alice', 'a@b.com', 'US');
expect(WebClient.instance.response.session.accountEditChanged).toHaveBeenCalledWith('Alice', 'a@b.com', 'US');
});
});
@ -91,40 +82,40 @@ describe('accountImage', () => {
it('sends Command_AccountImage', () => {
const img = new Uint8Array([1, 2]);
accountImage(img);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AccountImage_ext, expect.objectContaining({ image: img }), expect.any(Object)
);
});
it('calls SessionPersistence.accountImageChanged on success', () => {
it('calls WebClient.instance.response.session.accountImageChanged on success', () => {
const img = new Uint8Array([1, 2]);
accountImage(img);
invokeOnSuccess();
expect(SessionPersistence.accountImageChanged).toHaveBeenCalledWith(img);
expect(WebClient.instance.response.session.accountImageChanged).toHaveBeenCalledWith(img);
});
});
describe('accountPassword', () => {
it('sends Command_AccountPassword', () => {
accountPassword('old', 'new', 'hashed');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AccountPassword_ext,
expect.objectContaining({ oldPassword: 'old', newPassword: 'new', hashedNewPassword: 'hashed' }),
expect.any(Object)
);
});
it('calls SessionPersistence.accountPasswordChange on success', () => {
it('calls WebClient.instance.response.session.accountPasswordChange on success', () => {
accountPassword('old', 'new', 'hashed');
invokeOnSuccess();
expect(SessionPersistence.accountPasswordChange).toHaveBeenCalled();
expect(WebClient.instance.response.session.accountPasswordChange).toHaveBeenCalled();
});
});
describe('deckDel', () => {
it('sends Command_DeckDel', () => {
deckDel(42);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckDel_ext,
expect.objectContaining({ deckId: 42 }),
expect.any(Object)
@ -134,14 +125,14 @@ describe('deckDel', () => {
it('calls deleteServerDeck on success', () => {
deckDel(42);
invokeOnSuccess();
expect(SessionPersistence.deleteServerDeck).toHaveBeenCalledWith(42);
expect(WebClient.instance.response.session.deleteServerDeck).toHaveBeenCalledWith(42);
});
});
describe('deckDelDir', () => {
it('sends Command_DeckDelDir', () => {
deckDelDir('/path');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckDelDir_ext, expect.objectContaining({ path: '/path' }), expect.any(Object)
);
});
@ -149,14 +140,14 @@ describe('deckDelDir', () => {
it('calls deleteServerDeckDir on success', () => {
deckDelDir('/path');
invokeOnSuccess();
expect(SessionPersistence.deleteServerDeckDir).toHaveBeenCalledWith('/path');
expect(WebClient.instance.response.session.deleteServerDeckDir).toHaveBeenCalledWith('/path');
});
});
describe('deckList', () => {
it('sends Command_DeckList', () => {
deckList();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_DeckList_ext })
@ -167,14 +158,14 @@ describe('deckList', () => {
deckList();
const root = { items: [] };
invokeOnSuccess({ root }, { responseCode: 0 });
expect(SessionPersistence.updateServerDecks).toHaveBeenCalledWith({ root });
expect(WebClient.instance.response.session.updateServerDecks).toHaveBeenCalledWith({ root });
});
});
describe('deckNewDir', () => {
it('sends Command_DeckNewDir', () => {
deckNewDir('/path', 'dir');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckNewDir_ext, expect.objectContaining({ path: '/path', dirName: 'dir' }), expect.any(Object)
);
});
@ -182,14 +173,14 @@ describe('deckNewDir', () => {
it('calls createServerDeckDir on success', () => {
deckNewDir('/path', 'dir');
invokeOnSuccess();
expect(SessionPersistence.createServerDeckDir).toHaveBeenCalledWith('/path', 'dir');
expect(WebClient.instance.response.session.createServerDeckDir).toHaveBeenCalledWith('/path', 'dir');
});
});
describe('deckUpload', () => {
it('sends Command_DeckUpload', () => {
deckUpload('/path', 1, 'content');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_DeckUpload_ext,
expect.objectContaining({ path: '/path', deckId: 1, deckList: 'content' }),
expect.objectContaining({ responseExt: Data.Response_DeckUpload_ext })
@ -200,21 +191,21 @@ describe('deckUpload', () => {
deckUpload('/path', 1, 'content');
const resp = { newFile: { id: 1 } };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.uploadServerDeck).toHaveBeenCalledWith('/path', resp.newFile);
expect(WebClient.instance.response.session.uploadServerDeck).toHaveBeenCalledWith('/path', resp.newFile);
});
});
describe('disconnect', () => {
it('calls webClient.disconnect', () => {
it('calls WebClient.instance.disconnect', () => {
disconnect();
expect(webClient.disconnect).toHaveBeenCalled();
expect(WebClient.instance.disconnect).toHaveBeenCalled();
});
});
describe('getGamesOfUser', () => {
it('sends Command_GetGamesOfUser', () => {
getGamesOfUser('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_GetGamesOfUser_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_GetGamesOfUser_ext })
@ -225,14 +216,14 @@ describe('getGamesOfUser', () => {
getGamesOfUser('alice');
const resp = { gameList: [] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.getGamesOfUser).toHaveBeenCalledWith('alice', resp);
expect(WebClient.instance.response.session.getGamesOfUser).toHaveBeenCalledWith('alice', resp);
});
});
describe('getUserInfo', () => {
it('sends Command_GetUserInfo', () => {
getUserInfo('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_GetUserInfo_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_GetUserInfo_ext })
@ -243,57 +234,57 @@ describe('getUserInfo', () => {
getUserInfo('alice');
const resp = { userInfo: { name: 'alice' } };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.getUserInfo).toHaveBeenCalledWith(resp.userInfo);
expect(WebClient.instance.response.session.getUserInfo).toHaveBeenCalledWith(resp.userInfo);
});
});
describe('joinRoom', () => {
it('sends Command_JoinRoom', () => {
joinRoom(5);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_JoinRoom_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_JoinRoom_ext })
);
});
it('calls RoomPersistence.joinRoom on success', () => {
it('calls WebClient.instance.response.room.joinRoom on success', () => {
joinRoom(5);
const resp = { roomInfo: { roomId: 5 } };
invokeOnSuccess(resp, { responseCode: 0 });
expect(RoomPersistence.joinRoom).toHaveBeenCalledWith(resp.roomInfo);
expect(WebClient.instance.response.room.joinRoom).toHaveBeenCalledWith(resp.roomInfo);
});
});
describe('listRooms (command)', () => {
it('sends Command_ListRooms', () => {
listRooms();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(Data.Command_ListRooms_ext, expect.any(Object));
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(Data.Command_ListRooms_ext, expect.any(Object));
});
});
describe('listUsers', () => {
it('sends Command_ListUsers', () => {
listUsers();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ListUsers_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ListUsers_ext })
);
});
it('calls SessionPersistence.updateUsers with the user list on success', () => {
it('calls WebClient.instance.response.session.updateUsers with the user list on success', () => {
listUsers();
const resp = { userList: [{ name: 'Alice' }] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.updateUsers).toHaveBeenCalledWith([{ name: 'Alice' }]);
expect(WebClient.instance.response.session.updateUsers).toHaveBeenCalledWith([{ name: 'Alice' }]);
});
});
describe('message', () => {
it('sends Command_Message', () => {
message('bob', 'hi');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Message_ext, expect.objectContaining({ userName: 'bob', message: 'hi' })
);
});
@ -304,7 +295,9 @@ describe('ping', () => {
it('sends Command_Ping', () => {
const pingReceived = vi.fn();
ping(pingReceived);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(Data.Command_Ping_ext, expect.any(Object), expect.any(Object));
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_Ping_ext, expect.any(Object), expect.any(Object)
);
});
it('calls pingReceived via onResponse', () => {
@ -318,7 +311,7 @@ describe('ping', () => {
describe('replayDeleteMatch', () => {
it('sends Command_ReplayDeleteMatch', () => {
replayDeleteMatch(7);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayDeleteMatch_ext,
expect.objectContaining({ gameId: 7 }),
expect.any(Object)
@ -328,14 +321,14 @@ describe('replayDeleteMatch', () => {
it('calls replayDeleteMatch on success', () => {
replayDeleteMatch(7);
invokeOnSuccess();
expect(SessionPersistence.replayDeleteMatch).toHaveBeenCalledWith(7);
expect(WebClient.instance.response.session.replayDeleteMatch).toHaveBeenCalledWith(7);
});
});
describe('replayList', () => {
it('sends Command_ReplayList', () => {
replayList();
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayList_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ReplayList_ext })
@ -346,14 +339,14 @@ describe('replayList', () => {
replayList();
const resp = { matchList: [] };
invokeOnSuccess(resp, { responseCode: 0 });
expect(SessionPersistence.replayList).toHaveBeenCalledWith([]);
expect(WebClient.instance.response.session.replayList).toHaveBeenCalledWith([]);
});
});
describe('replayModifyMatch', () => {
it('sends Command_ReplayModifyMatch', () => {
replayModifyMatch(7, true);
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayModifyMatch_ext, expect.objectContaining({ gameId: 7, doNotHide: true }), expect.any(Object)
);
});
@ -361,14 +354,14 @@ describe('replayModifyMatch', () => {
it('calls replayModifyMatch on success', () => {
replayModifyMatch(7, true);
invokeOnSuccess();
expect(SessionPersistence.replayModifyMatch).toHaveBeenCalledWith(7, true);
expect(WebClient.instance.response.session.replayModifyMatch).toHaveBeenCalledWith(7, true);
});
});
describe('addToList / addToBuddyList / addToIgnoreList', () => {
it('addToBuddyList sends Command_AddToList with list=buddy', () => {
addToBuddyList('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AddToList_ext,
expect.objectContaining({ list: 'buddy' }),
expect.any(Object)
@ -377,24 +370,24 @@ describe('addToList / addToBuddyList / addToIgnoreList', () => {
it('addToIgnoreList sends Command_AddToList with list=ignore', () => {
addToIgnoreList('bob');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_AddToList_ext,
expect.objectContaining({ list: 'ignore' }),
expect.any(Object)
);
});
it('onSuccess calls SessionPersistence.addToList', () => {
it('onSuccess calls WebClient.instance.response.session.addToList', () => {
addToList('buddy', 'alice');
invokeOnSuccess();
expect(SessionPersistence.addToList).toHaveBeenCalledWith('buddy', 'alice');
expect(WebClient.instance.response.session.addToList).toHaveBeenCalledWith('buddy', 'alice');
});
});
describe('removeFromList / removeFromBuddyList / removeFromIgnoreList', () => {
it('removeFromBuddyList sends Command_RemoveFromList with list=buddy', () => {
removeFromBuddyList('alice');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_RemoveFromList_ext,
expect.objectContaining({ list: 'buddy' }),
expect.any(Object)
@ -403,24 +396,24 @@ describe('removeFromList / removeFromBuddyList / removeFromIgnoreList', () => {
it('removeFromIgnoreList sends Command_RemoveFromList with list=ignore', () => {
removeFromIgnoreList('bob');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_RemoveFromList_ext,
expect.objectContaining({ list: 'ignore' }),
expect.any(Object)
);
});
it('onSuccess calls SessionPersistence.removeFromList', () => {
it('onSuccess calls WebClient.instance.response.session.removeFromList', () => {
removeFromList('buddy', 'alice');
invokeOnSuccess();
expect(SessionPersistence.removeFromList).toHaveBeenCalledWith('buddy', 'alice');
expect(WebClient.instance.response.session.removeFromList).toHaveBeenCalledWith('buddy', 'alice');
});
});
describe('replayGetCode', () => {
it('sends Command_ReplayGetCode with gameId and responseExt', () => {
replayGetCode(42, vi.fn());
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplayGetCode_ext,
expect.any(Object),
expect.objectContaining({ responseExt: Data.Response_ReplayGetCode_ext })
@ -438,7 +431,7 @@ describe('replayGetCode', () => {
describe('replaySubmitCode', () => {
it('sends Command_ReplaySubmitCode with replayCode', () => {
replaySubmitCode('42-abc123');
expect(webClient.protobuf.sendSessionCommand).toHaveBeenCalledWith(
expect(WebClient.instance.protobuf.sendSessionCommand).toHaveBeenCalledWith(
Data.Command_ReplaySubmitCode_ext, expect.objectContaining({ replayCode: '42-abc123' }), expect.any(Object)
);
});