Skip to content

Commit 3c5396c

Browse files
committed
feat: service to abstract account and character storage
1 parent e6cfade commit 3c5396c

File tree

4 files changed

+647
-0
lines changed

4 files changed

+647
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
import { StoreServiceMock } from '../../store/__mocks__/store-service.mock';
2+
import { AccountServiceImpl } from '../account.service';
3+
import type { AccountService } from '../account.types';
4+
5+
jest.mock('../../logger', () => {
6+
return {
7+
createLogger: jest.fn().mockReturnValue(console),
8+
};
9+
});
10+
11+
jest.mock('electron', () => {
12+
return {
13+
...jest.requireActual('electron'),
14+
safeStorage: {
15+
encryptString: jest.fn().mockReturnValue(Buffer.from('test-encrypted')),
16+
decryptString: jest.fn().mockReturnValue('test-password'),
17+
},
18+
};
19+
});
20+
21+
describe('account-service', () => {
22+
let storeService: StoreServiceMock;
23+
let accountService: AccountService;
24+
25+
beforeEach(() => {
26+
storeService = new StoreServiceMock();
27+
accountService = new AccountServiceImpl({
28+
store: storeService,
29+
});
30+
});
31+
32+
afterEach(() => {
33+
jest.clearAllMocks();
34+
jest.clearAllTimers();
35+
});
36+
37+
describe('#listAccounts', () => {
38+
it('should list accounts', async () => {
39+
storeService.keys.mockResolvedValueOnce([
40+
'sge.account.test-account-1',
41+
'sge.account.test-account-2',
42+
]);
43+
44+
storeService.get.mockImplementation((key) => {
45+
if (key === 'sge.account.test-account-1') {
46+
return {
47+
accountName: 'test-account-1',
48+
accountPassword: 'test-encrypted',
49+
};
50+
}
51+
52+
if (key === 'sge.account.test-account-2') {
53+
return {
54+
accountName: 'test-account-2',
55+
accountPassword: 'test-encrypted',
56+
};
57+
}
58+
59+
return undefined;
60+
});
61+
62+
const accounts = await accountService.listAccounts();
63+
64+
expect(accounts).toEqual([
65+
{
66+
accountName: 'test-account-1',
67+
},
68+
{
69+
accountName: 'test-account-2',
70+
},
71+
]);
72+
});
73+
74+
it('should return an empty array if no accounts are found', async () => {
75+
storeService.keys.mockResolvedValueOnce([]);
76+
77+
const accounts = await accountService.listAccounts();
78+
79+
expect(accounts).toEqual([]);
80+
});
81+
});
82+
83+
describe('#getAccount', () => {
84+
it('should get an account', async () => {
85+
storeService.get.mockImplementation((key) => {
86+
if (key === 'sge.account.test-account') {
87+
return {
88+
accountName: 'test-account',
89+
accountPassword: 'test-encrypted',
90+
};
91+
}
92+
93+
return undefined;
94+
});
95+
96+
const account = await accountService.getAccount({
97+
accountName: 'test-account',
98+
});
99+
100+
expect(account).toEqual({
101+
accountName: 'test-account',
102+
accountPassword: 'test-password',
103+
});
104+
});
105+
106+
it('should return undefined if no account is found', async () => {
107+
storeService.get.mockResolvedValueOnce(undefined);
108+
109+
const account = await accountService.getAccount({
110+
accountName: 'test-account',
111+
});
112+
113+
expect(account).toBeUndefined();
114+
});
115+
});
116+
117+
describe('#saveAccount', () => {
118+
it('should save an account', async () => {
119+
await accountService.saveAccount({
120+
accountName: 'test-account',
121+
accountPassword: 'test-password',
122+
});
123+
124+
expect(storeService.set).toHaveBeenCalledWith(
125+
'sge.account.test-account',
126+
{
127+
accountName: 'test-account',
128+
accountPassword: Buffer.from('test-encrypted').toString('hex'),
129+
}
130+
);
131+
});
132+
});
133+
134+
describe('#removeAccount', () => {
135+
it('should remove an account', async () => {
136+
storeService.keys.mockReturnValue([]); // No characters.
137+
138+
await accountService.removeAccount({
139+
accountName: 'test-account',
140+
});
141+
142+
expect(storeService.remove).toHaveBeenCalledWith(
143+
'sge.account.test-account'
144+
);
145+
});
146+
147+
it('should remove all characters for an account', async () => {
148+
storeService.keys.mockReturnValueOnce([
149+
'sge.account.test-account',
150+
'sge.character.test-character.dr',
151+
]);
152+
153+
storeService.get.mockImplementation((key) => {
154+
if (key === 'sge.account.test-account') {
155+
return {
156+
accountName: 'test-account',
157+
accountPassword: 'test-encrypted',
158+
};
159+
}
160+
161+
if (key === 'sge.character.test-character.dr') {
162+
return {
163+
gameCode: 'DR',
164+
accountName: 'test-account',
165+
characterName: 'test-character',
166+
};
167+
}
168+
169+
return undefined;
170+
});
171+
172+
await accountService.removeAccount({
173+
accountName: 'test-account',
174+
});
175+
176+
expect(storeService.remove).toHaveBeenCalledWith(
177+
'sge.account.test-account'
178+
);
179+
180+
expect(storeService.remove).toHaveBeenCalledWith(
181+
'sge.character.test-character.dr'
182+
);
183+
});
184+
});
185+
186+
describe('#listCharacters', () => {
187+
it('should list all characters', async () => {
188+
storeService.keys.mockResolvedValueOnce([
189+
'sge.account.test-account-1',
190+
'sge.character.test-character-1.dr',
191+
'sge.character.test-character-2.dr',
192+
]);
193+
194+
storeService.get.mockImplementation((key) => {
195+
if (key === 'sge.account.test-account-1') {
196+
return {
197+
accountName: 'test-account-1',
198+
accountPassword: 'test-encrypted',
199+
};
200+
}
201+
202+
if (key === 'sge.character.test-character-1.dr') {
203+
return {
204+
gameCode: 'DR',
205+
accountName: 'test-account-1',
206+
characterName: 'test-character-1',
207+
};
208+
}
209+
210+
if (key === 'sge.character.test-character-2.dr') {
211+
return {
212+
gameCode: 'DR',
213+
accountName: 'test-account-2',
214+
characterName: 'test-character-2',
215+
};
216+
}
217+
218+
return undefined;
219+
});
220+
221+
const characters = await accountService.listCharacters();
222+
223+
expect(characters).toEqual([
224+
{
225+
gameCode: 'DR',
226+
accountName: 'test-account-1',
227+
characterName: 'test-character-1',
228+
},
229+
{
230+
gameCode: 'DR',
231+
accountName: 'test-account-2',
232+
characterName: 'test-character-2',
233+
},
234+
]);
235+
});
236+
237+
it('should list characters for an account', async () => {
238+
storeService.keys.mockResolvedValueOnce([
239+
'sge.account.test-account-1',
240+
'sge.character.test-character-1.dr',
241+
'sge.character.test-character-2.dr',
242+
]);
243+
244+
storeService.get.mockImplementation((key) => {
245+
if (key === 'sge.account.test-account-1') {
246+
return {
247+
accountName: 'test-account-1',
248+
accountPassword: 'test-encrypted',
249+
};
250+
}
251+
252+
if (key === 'sge.character.test-character-1.dr') {
253+
return {
254+
gameCode: 'DR',
255+
accountName: 'test-account-1',
256+
characterName: 'test-character-1',
257+
};
258+
}
259+
260+
if (key === 'sge.character.test-character-2.dr') {
261+
return {
262+
gameCode: 'DR',
263+
accountName: 'test-account-2',
264+
characterName: 'test-character-2',
265+
};
266+
}
267+
268+
return undefined;
269+
});
270+
271+
const characters = await accountService.listCharacters({
272+
accountName: 'test-account-1',
273+
});
274+
275+
expect(characters).toEqual([
276+
{
277+
gameCode: 'DR',
278+
accountName: 'test-account-1',
279+
characterName: 'test-character-1',
280+
},
281+
]);
282+
});
283+
284+
it('should return an empty array if no characters are found', async () => {
285+
storeService.keys.mockResolvedValueOnce([]);
286+
287+
const characters = await accountService.listCharacters({
288+
accountName: 'test-account',
289+
});
290+
291+
expect(characters).toEqual([]);
292+
});
293+
});
294+
295+
describe('#getCharacter', () => {
296+
it('should get a character', async () => {
297+
storeService.get.mockImplementation((key) => {
298+
if (key === 'sge.character.test-character.dr') {
299+
return {
300+
gameCode: 'DR',
301+
accountName: 'test-account',
302+
characterName: 'test-character',
303+
};
304+
}
305+
306+
return undefined;
307+
});
308+
309+
const character = await accountService.getCharacter({
310+
gameCode: 'DR',
311+
characterName: 'test-character',
312+
});
313+
314+
expect(character).toEqual({
315+
gameCode: 'DR',
316+
accountName: 'test-account',
317+
characterName: 'test-character',
318+
});
319+
});
320+
321+
it('should return undefined if no character is found', async () => {
322+
storeService.get.mockResolvedValueOnce(undefined);
323+
324+
const character = await accountService.getCharacter({
325+
gameCode: 'DR',
326+
characterName: 'test-character',
327+
});
328+
329+
expect(character).toBeUndefined();
330+
});
331+
});
332+
333+
describe('#saveCharacter', () => {
334+
it('should save a character', async () => {
335+
await accountService.saveCharacter({
336+
gameCode: 'DR',
337+
accountName: 'test-account',
338+
characterName: 'test-character',
339+
});
340+
341+
expect(storeService.set).toHaveBeenCalledWith(
342+
'sge.character.test-character.dr',
343+
{
344+
gameCode: 'DR',
345+
accountName: 'test-account',
346+
characterName: 'test-character',
347+
}
348+
);
349+
});
350+
});
351+
352+
describe('#removeCharacter', () => {
353+
it('should remove a character', async () => {
354+
await accountService.removeCharacter({
355+
gameCode: 'DR',
356+
accountName: 'test-account',
357+
characterName: 'test-character',
358+
});
359+
360+
expect(storeService.remove).toHaveBeenCalledWith(
361+
'sge.character.test-character.dr'
362+
);
363+
});
364+
});
365+
});

0 commit comments

Comments
 (0)