test: cut low-value tests, rewrite the rest around behavior, parallelize lanes (#285)

This commit is contained in:
sudacode authored and GitHub committed 2026-10-05 00:07:44 -07:00
1 parent 4b6842d034
commit b48083ddd8
368 files changed
+18045 -60331

No files matched your search

@@ -2,9 +2,13 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import { CardCreationService } from './card-creation';
import type { AnkiConnectConfig } from '../types/anki';
type CardCreationDeps = ConstructorParameters<typeof CardCreationService>[0];
import {
type CardCreationDeps,
createAnkiConfig,
createCardCreationDeps,
createFakeClient,
createFakeMediaGenerator,
} from './card-creation-test-utils';
test('sentence card writes generated audio only to sentence audio field', async () => {
const addedFields: Record<string, string>[] = [];
@@ -13,128 +17,88 @@ test('sentence card writes generated audio only to sentence audio field', async
const requestedProperties: string[] = [];
const audioVolumeScales: Array<number | undefined> = [];
const audioRanges: Array<{ start: number; end: number; padding: number | undefined }> = [];
const deps: CardCreationDeps = {
getConfig: () =>
({
deck: 'Mining',
fields: {
word: 'Expression',
sentence: 'Sentence',
audio: 'ExpressionAudio',
translation: 'SelectionText',
},
media: {
generateAudio: true,
generateImage: false,
mirrorMpvVolume: true,
maxMediaDuration: 30,
},
behavior: {},
ai: false,
}) as AnkiConnectConfig,
getAiConfig: () => ({}),
getTimingTracker: () => ({}) as never,
getMpvClient: () =>
({
currentVideoPath: '/video.mp4',
currentSubText: '字幕',
currentSubStart: 12,
currentSubEnd: 14,
currentTimePos: 13,
currentAudioStreamIndex: 0,
requestProperty: async (name: string) => {
requestedProperties.push(name);
return 40;
},
}) as never,
client: {
addNote: async (_deck, _modelName, fields) => {
addedFields.push(fields);
return 42;
},
addTags: async () => undefined,
notesInfo: async () => [
{
noteId: 42,
fields: {
Expression: { value: '字幕' },
Sentence: { value: '字幕' },
SelectionText: { value: 'Subtitle' },
ExpressionAudio: { value: '' },
SentenceAudio: { value: '' },
},
},
],
updateNoteFields: async (_noteId, fields) => {
updatedFields.push(fields);
},
storeMediaFile: async (filename) => {
storedMedia.push(filename);
},
findNotes: async () => [],
retrieveMediaFile: async () => '',
deleteNotes: async () => undefined,
},
mediaGenerator: {
generateAudio: async (
_path,
startTime,
endTime,
audioPadding,
_audioStreamIndex,
_normalizeAudio,
volumeScale,
) => {
audioRanges.push({ start: startTime, end: endTime, padding: audioPadding });
audioVolumeScales.push(volumeScale);
return Buffer.from('audio');
},
generateScreenshot: async () => null,
generateAnimatedImage: async () => null,
},
showOsdNotification: () => undefined,
showUpdateResult: () => undefined,
showStatusNotification: () => undefined,
showNotification: async () => undefined,
beginUpdateProgress: () => undefined,
endUpdateProgress: () => undefined,
withUpdateProgress: async (_message, action) => action(),
resolveConfiguredFieldName: (noteInfo, ...preferredNames) => {
for (const preferredName of preferredNames) {
if (preferredName && preferredName in noteInfo.fields) return preferredName;
}
return null;
},
resolveNoteFieldName: (noteInfo, preferredName) =>
preferredName && preferredName in noteInfo.fields ? preferredName : null,
getAnimatedImageLeadInSeconds: async () => 0,
extractFields: () => ({}),
processSentence: (sentence) => sentence,
setCardTypeFields: () => undefined,
mergeFieldValue: (_existing, newValue) => newValue,
formatMiscInfoPattern: () => '',
getEffectiveSentenceCardConfig: () => ({
model: 'Sentence',
sentenceField: 'Sentence',
audioField: 'SentenceAudio',
lapisEnabled: true,
kikuEnabled: false,
fieldGroupingMode: 'disabled',
}),
getFallbackDurationSeconds: () => 10,
appendKnownWordsFromNoteInfo: () => undefined,
removeKnownWordNote: () => undefined,
isUpdateInProgress: () => false,
setUpdateInProgress: () => undefined,
trackLastAddedNoteId: () => undefined,
reviewMediaTiming: async () => ({ action: 'confirm', startTime: 11.4, endTime: 14.2 }),
let review: Awaited<ReturnType<NonNullable<CardCreationDeps['reviewMediaTiming']>>> = {
action: 'confirm',
startTime: 11.4,
endTime: 14.2,
};
const service = new CardCreationService(deps);
const created = await service.createSentenceCard('字幕', 12, 14, 'Subtitle');
const service = new CardCreationService(
createCardCreationDeps({
getConfig: () =>
createAnkiConfig({
fields: {
word: 'Expression',
audio: 'ExpressionAudio',
translation: 'SelectionText',
},
media: { generateAudio: true, mirrorMpvVolume: true, maxMediaDuration: 30 },
}),
getMpvClient: () =>
({
currentVideoPath: '/video.mp4',
currentSubText: '字幕',
currentSubStart: 12,
currentSubEnd: 14,
currentTimePos: 13,
currentAudioStreamIndex: 0,
requestProperty: async (name: string) => {
requestedProperties.push(name);
return 40;
},
}) as never,
client: createFakeClient({
addNote: async (_deck, _modelName, fields) => {
addedFields.push(fields);
return 42;
},
notesInfo: async () => [
{
noteId: 42,
fields: {
Expression: { value: '字幕' },
Sentence: { value: '字幕' },
SelectionText: { value: 'Subtitle' },
ExpressionAudio: { value: '' },
SentenceAudio: { value: '' },
},
},
],
updateNoteFields: async (_noteId, fields) => {
updatedFields.push(fields);
},
storeMediaFile: async (filename) => {
storedMedia.push(filename);
},
}),
mediaGenerator: createFakeMediaGenerator({
generateAudio: async (
_path,
startTime,
endTime,
audioPadding,
_audioStreamIndex,
_normalizeAudio,
volumeScale,
) => {
audioRanges.push({ start: startTime, end: endTime, padding: audioPadding });
audioVolumeScales.push(volumeScale);
return Buffer.from('audio');
},
}),
getEffectiveSentenceCardConfig: () => ({
model: 'Sentence',
sentenceField: 'Sentence',
audioField: 'SentenceAudio',
lapisEnabled: true,
kikuEnabled: false,
fieldGroupingMode: 'disabled',
}),
reviewMediaTiming: async () => review,
}),
);
assert.equal(created, true);
assert.equal(await service.createSentenceCard('字幕', 12, 14, 'Subtitle'), true);
assert.deepEqual(addedFields[0], {
Sentence: '字幕',
SelectionText: 'Subtitle',
@@ -149,11 +113,11 @@ test('sentence card writes generated audio only to sentence audio field', async
assert.equal(mediaUpdate?.SentenceAudio, `[sound:${storedMedia[0]}]`);
assert.equal('ExpressionAudio' in mediaUpdate!, false);
deps.reviewMediaTiming = async () => ({ action: 'discard' });
review = { action: 'discard' };
assert.equal(await service.createSentenceCard('作らない', 20, 22), false);
assert.equal(addedFields.length, 1);
deps.reviewMediaTiming = async () => ({ action: 'skip-media' });
review = { action: 'skip-media' };
assert.equal(await service.createSentenceCard('メディアなし', 30, 32), true);
assert.equal(addedFields.length, 2);
assert.equal(storedMedia.length, 1);