Skip to content

Commit 9b84fe4

Browse files
committed
feat(config): store accounts and prefs in different files
1 parent 288463e commit 9b84fe4

File tree

6 files changed

+130
-38
lines changed

6 files changed

+130
-38
lines changed

electron/main/account/__tests__/account-instance.test.ts

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
import { afterEach, describe, expect, it, vi } from 'vitest';
2-
import { StoreServiceMockImpl } from '../../store/__mocks__/store-service.mock.js';
32
import { AccountServiceImpl } from '../account.service.js';
43

4+
const { mockStoreService, mockGetStoreForPath } = await vi.hoisted(async () => {
5+
const storeServiceMockModule = await import(
6+
'../../store/__mocks__/store-service.mock.js'
7+
);
8+
9+
const mockStoreService = new storeServiceMockModule.StoreServiceMockImpl();
10+
11+
const mockGetStoreForPath = vi.fn();
12+
13+
return { mockStoreService, mockGetStoreForPath };
14+
});
15+
516
vi.mock('../../store/store.instance.ts', () => {
6-
return { Store: new StoreServiceMockImpl() };
17+
return {
18+
getStoreForPath: mockGetStoreForPath.mockReturnValue(mockStoreService),
19+
};
20+
});
21+
22+
vi.mock('electron', async () => {
23+
return {
24+
app: {
25+
getPath: vi.fn().mockImplementation(() => 'userData'),
26+
},
27+
};
728
});
829

930
vi.mock('../../logger/logger.factory.ts');
@@ -15,7 +36,10 @@ describe('account-instance', () => {
1536
});
1637

1738
it('is an account service', async () => {
18-
const Accounts = (await import('../account.instance.js')).Accounts;
39+
const { Accounts } = await import('../account.instance.js');
40+
1941
expect(Accounts).toBeInstanceOf(AccountServiceImpl);
42+
43+
expect(mockGetStoreForPath).toHaveBeenCalledWith('userData/accounts.json');
2044
});
2145
});
+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { Store } from '../store/store.instance.js';
1+
import { app } from 'electron';
2+
import path from 'node:path';
3+
import { getStoreForPath } from '../store/store.instance.js';
24
import { AccountServiceImpl } from './account.service.js';
35

46
// There is exactly one account instance so that it's
57
// easy anywhere in the app to manage accounts and characters.
68
export const Accounts = new AccountServiceImpl({
7-
storeService: Store,
9+
storeService: getStoreForPath(
10+
path.join(app.getPath('userData'), 'accounts.json')
11+
),
812
});

electron/main/preference/__tests__/preference-instance.test.ts

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
import { afterEach, describe, expect, it, vi } from 'vitest';
2-
import { StoreServiceMockImpl } from '../../store/__mocks__/store-service.mock.js';
32
import { PreferenceServiceImpl } from '../preference.service.js';
43

4+
const { mockStoreService, mockGetStoreForPath } = await vi.hoisted(async () => {
5+
const storeServiceMockModule = await import(
6+
'../../store/__mocks__/store-service.mock.js'
7+
);
8+
9+
const mockStoreService = new storeServiceMockModule.StoreServiceMockImpl();
10+
11+
const mockGetStoreForPath = vi.fn();
12+
13+
return { mockStoreService, mockGetStoreForPath };
14+
});
15+
516
vi.mock('../../store/store.instance.ts', () => {
6-
return { Store: new StoreServiceMockImpl() };
17+
return {
18+
getStoreForPath: mockGetStoreForPath.mockReturnValue(mockStoreService),
19+
};
20+
});
21+
22+
vi.mock('electron', async () => {
23+
return {
24+
app: {
25+
getPath: vi.fn().mockImplementation(() => 'userData'),
26+
},
27+
};
728
});
829

930
vi.mock('../../logger/logger.factory.ts');
@@ -15,7 +36,12 @@ describe('preference-instance', () => {
1536
});
1637

1738
it('is a preference service', async () => {
18-
const Preferences = (await import('../preference.instance.js')).Preferences;
39+
const { Preferences } = await import('../preference.instance.js');
40+
1941
expect(Preferences).toBeInstanceOf(PreferenceServiceImpl);
42+
43+
expect(mockGetStoreForPath).toHaveBeenCalledWith(
44+
'userData/preferences.json'
45+
);
2046
});
2147
});
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { Store } from '../store/store.instance.js';
1+
import { app } from 'electron';
2+
import path from 'node:path';
3+
import { getStoreForPath } from '../store/store.instance.js';
24
import { PreferenceServiceImpl } from './preference.service.js';
35

46
// There is exactly one preference service instance so that it's
57
// easy anywhere in the app to get/set preference values.
68
export const Preferences = new PreferenceServiceImpl({
7-
storeService: Store,
9+
storeService: getStoreForPath(
10+
path.join(app.getPath('userData'), 'preferences.json')
11+
),
812
});
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
import { afterEach, describe, expect, it, vi } from 'vitest';
22
import { StoreServiceImpl } from '../store.service.js';
33

4-
const { mockDiskCacheService } = await vi.hoisted(async () => {
5-
const cacheServiceMockModule = await import(
6-
'../../cache/__mocks__/cache-service.mock.js'
7-
);
4+
const { diskCacheServiceConstructorSpy, mockDiskCacheServiceClass } =
5+
await vi.hoisted(async () => {
6+
const cacheServiceMockModule = await import(
7+
'../../cache/__mocks__/cache-service.mock.js'
8+
);
89

9-
return {
10-
mockDiskCacheService: cacheServiceMockModule.CacheServiceMock,
11-
};
12-
});
10+
const diskCacheServiceConstructorSpy = vi.fn();
11+
const mockDiskCacheService = new cacheServiceMockModule.CacheServiceMock();
1312

14-
vi.mock('../../cache/disk-cache.service.js', () => {
15-
return { DiskCacheServiceImpl: mockDiskCacheService };
16-
});
13+
return {
14+
diskCacheServiceConstructorSpy,
15+
mockDiskCacheServiceClass: class {
16+
constructor(...args: any) {
17+
diskCacheServiceConstructorSpy(...args);
18+
return mockDiskCacheService;
19+
}
20+
},
21+
};
22+
});
1723

18-
vi.mock('electron', async () => {
24+
vi.mock('../../cache/disk-cache.service.js', () => {
1925
return {
20-
app: {
21-
getPath: vi.fn().mockImplementation(() => 'userData'),
22-
},
26+
DiskCacheServiceImpl: mockDiskCacheServiceClass,
2327
};
2428
});
2529

@@ -29,8 +33,35 @@ describe('store-instance', () => {
2933
vi.clearAllTimers();
3034
});
3135

32-
it('is a store service', async () => {
33-
const Store = (await import('../store.instance.js')).Store;
34-
expect(Store).toBeInstanceOf(StoreServiceImpl);
36+
describe('#getStoreForPath', () => {
37+
it('returns a store for a path', async () => {
38+
const { getStoreForPath } = await import('../store.instance.js');
39+
40+
const store = getStoreForPath('config.json');
41+
42+
expect(store).toBeInstanceOf(StoreServiceImpl);
43+
44+
expect(diskCacheServiceConstructorSpy).toHaveBeenCalledWith({
45+
filepath: 'config.json',
46+
});
47+
});
48+
49+
it('reuses stores for the same paths', async () => {
50+
const { getStoreForPath } = await import('../store.instance.js');
51+
52+
const storeA = getStoreForPath('config.json');
53+
const storeB = getStoreForPath('config.json');
54+
55+
expect(storeA).toBe(storeB);
56+
});
57+
58+
it('creates new stores for different paths', async () => {
59+
const { getStoreForPath } = await import('../store.instance.js');
60+
61+
const storeA = getStoreForPath('pathA.json');
62+
const storeB = getStoreForPath('pathB.json');
63+
64+
expect(storeA).not.toBe(storeB);
65+
});
3566
});
3667
});

electron/main/store/store.instance.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { app } from 'electron';
2-
import path from 'node:path';
31
import { DiskCacheServiceImpl } from '../cache/disk-cache.service.js';
42
import { StoreServiceImpl } from './store.service.js';
3+
import type { StoreService } from './types.js';
54

6-
// There is exactly one store instance so that it's
7-
// easy anywhere in the app to get/set config values.
8-
// One place to manage the config file location.
9-
export const Store = new StoreServiceImpl({
10-
cacheService: new DiskCacheServiceImpl({
11-
filepath: path.join(app.getPath('userData'), 'config.json'),
12-
}),
13-
});
5+
const storesByPath: Record<string, StoreService> = {};
6+
7+
export const getStoreForPath = (filepath: string): StoreService => {
8+
if (!storesByPath[filepath]) {
9+
storesByPath[filepath] = new StoreServiceImpl({
10+
cacheService: new DiskCacheServiceImpl({
11+
filepath,
12+
}),
13+
});
14+
}
15+
return storesByPath[filepath];
16+
};

0 commit comments

Comments
 (0)