fix(dictionary): complete Hachidori imports and mining integration

This commit is contained in:
2026-09-22 15:01:23 -07:00
parent 15c74ef502
commit ab5e50adac
44 changed files with 756 additions and 40 deletions
@@ -31,6 +31,45 @@ function createDeferred<T>(): { promise: Promise<T>; resolve: (value: T) => void
return { promise, resolve };
}
test('replacement-capable imports retain the installed dictionary when an update fails', async () => {
for (const succeeds of [true, false]) {
let imported = false;
const runtime = createCharacterDictionaryAutoSyncRuntimeService({
userDataPath: makeTempDir(),
getConfig: () => ({ enabled: true, maxLoaded: 3, profileScope: 'all' }),
getOrCreateCurrentSnapshot: async () => ({
mediaId: 7,
mediaTitle: 'Frieren',
entryCount: 100,
fromCache: true,
updatedAt: 1000,
}),
buildMergedDictionary: async () => ({
zipPath: '/tmp/replacement.zip',
revision: 'new',
dictionaryTitle: 'SubMiner Character Dictionary',
entryCount: 100,
}),
getYomitanDictionaryInfo: async () => [
{ title: 'SubMiner Character Dictionary', revision: 'old' },
],
dictionaryImportReplacesExisting: () => true,
importYomitanDictionary: async () => {
imported = true;
return succeeds;
},
deleteYomitanDictionary: async () => {
assert.fail('must keep the old dictionary until replacement succeeds');
},
upsertYomitanDictionarySettings: async () => true,
now: () => 1000,
});
if (succeeds) await runtime.runSyncNow();
else await assert.rejects(runtime.runSyncNow(), /Failed to import dictionary ZIP/);
assert.equal(imported, true);
}
});
test('character dictionary manager snapshots, reorders, and removes MRU entries', () => {
const userDataPath = makeTempDir();
const statePath = path.join(userDataPath, 'character-dictionaries', 'auto-sync-state.json');