test(dictionary): pin concurrent snapshot writers to one surviving snapshot

Give each concurrent writer a distinct title, length, and term text so the
assertion identifies which writer's snapshot survived instead of only
checking that the file parses with the expected entry count.
This commit is contained in:
2026-08-17 11:10:38 -07:00
parent 9df2f37d4b
commit 354e3ea2e5
@@ -3,6 +3,7 @@ import * as fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import test from 'node:test'; import test from 'node:test';
import { isDeepStrictEqual } from 'node:util';
import { getSnapshotPath, readSnapshot, writeSnapshot } from './cache'; import { getSnapshotPath, readSnapshot, writeSnapshot } from './cache';
import { CHARACTER_DICTIONARY_FORMAT_VERSION } from './constants'; import { CHARACTER_DICTIONARY_FORMAT_VERSION } from './constants';
import type { CharacterDictionarySnapshot } from './types'; import type { CharacterDictionarySnapshot } from './types';
@@ -46,31 +47,46 @@ test('concurrent writeSnapshot calls for the same media leave one complete snaps
const outputDir = makeTempDir(); const outputDir = makeTempDir();
const snapshotPath = getSnapshotPath(outputDir, 130298); const snapshotPath = getSnapshotPath(outputDir, 130298);
const base = createSnapshot(); const base = createSnapshot();
const wide: CharacterDictionarySnapshot = { // Distinct titles, lengths, and term text so the surviving file can be pinned to exactly one
...base, // writer rather than merely "a snapshot that parses". A shared temp file is caught by the
entryCount: 400, // losing writers failing to rename; interleaved content is only caught when the timing happens
termEntries: Array.from({ length: 400 }, (_entry, index) => [ // to leave a mix, which is why the assertion checks identity rather than shape.
`名前${index}`, const variants: CharacterDictionarySnapshot[] = ['alpha', 'beta', 'gamma'].map((label, index) => {
'なまえ', const entryCount = 400 + index * 100;
'name primary', return {
'', ...base,
75, mediaTitle: `${base.mediaTitle} ${label}`,
[`Character ${index}`.repeat(200)], entryCount,
0, termEntries: Array.from({ length: entryCount }, (_entry, entryIndex) => [
'', `${label}${entryIndex}`,
]) as CharacterDictionarySnapshot['termEntries'], 'なまえ',
}; 'name primary',
'',
75,
[`${label} character ${entryIndex} `.repeat(600)],
0,
'',
]) as CharacterDictionarySnapshot['termEntries'],
};
});
await Promise.all([ await Promise.all(variants.map((variant) => writeSnapshot(snapshotPath, variant)));
writeSnapshot(snapshotPath, wide),
writeSnapshot(snapshotPath, wide),
writeSnapshot(snapshotPath, wide),
]);
const restored = await readSnapshot(snapshotPath); const restored = await readSnapshot(snapshotPath);
assert.equal(restored?.entryCount, 400); const expected = variants.map((variant) => ({
assert.equal(restored?.termEntries.length, 400); ...variant,
assert.deepEqual(restored, { ...wide, nameSplitSource: 'heuristic' }); nameSplitSource: 'heuristic' as const,
}));
const matches = expected.filter((candidate) => isDeepStrictEqual(restored, candidate));
assert.equal(
matches.length,
1,
`expected exactly one writer's complete snapshot to survive, got ${
restored === null
? 'an unreadable file'
: `entryCount=${restored.entryCount}, terms=${restored.termEntries.length}, title=${restored.mediaTitle}`
}`,
);
// Every writer cleaned up after itself, so no temp files are left behind. // Every writer cleaned up after itself, so no temp files are left behind.
const leftovers = fs const leftovers = fs