Files
SubMiner/src/main/character-dictionary-runtime/manual-selection-snapshot.test.ts
T

233 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import test from 'node:test';
import { createCharacterDictionaryRuntimeService } from '../character-dictionary-runtime';
import { buildCharacterDictionarySeriesKey } from './manual-selection';
function makeTempDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-character-dictionary-'));
}
test('getManualSelectionSnapshot waits for explicit search text before fetching candidates', async () => {
const userDataPath = makeTempDir();
const originalFetch = globalThis.fetch;
const searchTerms: string[] = [];
globalThis.fetch = (async (_input: string | URL | Request, init?: RequestInit) => {
const body = JSON.parse(String(init?.body ?? '{}')) as {
variables?: { search?: string };
};
searchTerms.push(String(body.variables?.search ?? ''));
return new Response(
JSON.stringify({
data: {
Page: {
media: [
{
id: 154587,
episodes: 28,
title: {
romaji: 'Sousou no Frieren',
english: 'Frieren: Beyond Journeys End',
native: '葬送のフリーレン',
},
},
],
},
},
}),
{
status: 200,
headers: { 'content-type': 'application/json' },
},
);
}) as typeof globalThis.fetch;
try {
const runtime = createCharacterDictionaryRuntimeService({
userDataPath,
getCurrentMediaPath: () => '/tmp/[SubsPlease] Kage no Jitsuryokusha - 05.mkv',
getCurrentMediaTitle: () => '[SubsPlease] Kage no Jitsuryokusha - 05.mkv',
resolveMediaPathForJimaku: (mediaPath) => mediaPath,
guessAnilistMediaInfo: async () => ({
title: 'Kage no Jitsuryokusha ni Naritakute!',
season: null,
episode: 5,
source: 'guessit',
}),
now: () => 1_700_000_000_000,
});
const initial = await runtime.getManualSelectionSnapshot(undefined, '');
assert.equal(initial.guessTitle, 'Kage no Jitsuryokusha ni Naritakute!');
assert.deepEqual(initial.candidates, []);
assert.deepEqual(searchTerms, []);
const searched = await runtime.getManualSelectionSnapshot(undefined, 'Frieren');
assert.deepEqual(searchTerms, ['Frieren']);
assert.deepEqual(searched.candidates, [
{ id: 154587, title: 'Frieren: Beyond Journeys End', episodes: 28 },
]);
} finally {
globalThis.fetch = originalFetch;
}
});
test('getManualSelectionSnapshot hydrates override episode count from searched candidates', async () => {
const userDataPath = makeTempDir();
const overrideSeriesKey = buildCharacterDictionarySeriesKey({
mediaPath: '/tmp/KonoSuba - 01.mkv',
mediaTitle: 'KonoSuba - 01.mkv',
guess: {
title: "KonoSuba - God's blessing on this wonderful world!",
year: 2016,
season: null,
episode: 1,
source: 'guessit',
},
});
const overrideDir = path.join(userDataPath, 'character-dictionaries');
fs.mkdirSync(overrideDir, { recursive: true });
fs.writeFileSync(
path.join(overrideDir, 'anilist-overrides.json'),
JSON.stringify({
overrides: [
{
seriesKey: overrideSeriesKey,
mediaId: 21202,
mediaTitle: "KONOSUBA -God's blessing on this wonderful world!",
staleMediaIds: [],
},
],
}),
'utf8',
);
const originalFetch = globalThis.fetch;
globalThis.fetch = (async (_input: string | URL | Request) => {
return new Response(
JSON.stringify({
data: {
Page: {
media: [
{
id: 21202,
episodes: 10,
title: {
romaji: 'Kono Subarashii Sekai ni Shukufuku wo!',
english: "KONOSUBA -God's blessing on this wonderful world!",
native: 'この素晴らしい世界に祝福を!',
},
},
],
},
},
}),
{
status: 200,
headers: { 'content-type': 'application/json' },
},
);
}) as typeof globalThis.fetch;
try {
const runtime = createCharacterDictionaryRuntimeService({
userDataPath,
getCurrentMediaPath: () => '/tmp/KonoSuba - 01.mkv',
getCurrentMediaTitle: () => 'KonoSuba - 01.mkv',
resolveMediaPathForJimaku: (mediaPath) => mediaPath,
guessAnilistMediaInfo: async () => ({
title: "KonoSuba - God's blessing on this wonderful world!",
year: 2016,
season: null,
episode: 1,
source: 'guessit',
}),
now: () => 1_700_000_000_000,
});
const snapshot = await runtime.getManualSelectionSnapshot(undefined, 'KonoSuba');
assert.deepEqual(snapshot.override, {
id: 21202,
title: "KONOSUBA -God's blessing on this wonderful world!",
episodes: 10,
});
} finally {
globalThis.fetch = originalFetch;
}
});
test('resolvePinnedMediaId returns the manual override for the current season', async () => {
const userDataPath = makeTempDir();
const seasonThreePath = '/anime/Oregairu/My Teen Romantic Comedy SNAFU (2013) - S03E01.mkv';
const guess = {
title: 'My Teen Romantic Comedy SNAFU',
year: 2013,
season: 3,
episode: 1,
source: 'guessit' as const,
};
const runtime = createCharacterDictionaryRuntimeService({
userDataPath,
getCurrentMediaPath: () => seasonThreePath,
getCurrentMediaTitle: () => null,
resolveMediaPathForJimaku: (mediaPath) => mediaPath,
guessAnilistMediaInfo: async () => guess,
now: () => 1_700_000_000_000,
});
assert.equal(
await runtime.resolvePinnedMediaId({
mediaPath: seasonThreePath,
mediaTitle: null,
guess,
}),
null,
);
const overridesPath = path.join(userDataPath, 'character-dictionaries', 'anilist-overrides.json');
fs.mkdirSync(path.dirname(overridesPath), { recursive: true });
fs.writeFileSync(
overridesPath,
JSON.stringify({
overrides: [
{
seriesKey: buildCharacterDictionarySeriesKey({
mediaPath: seasonThreePath,
mediaTitle: null,
guess,
}),
mediaId: 108489,
mediaTitle: 'My Teen Romantic Comedy SNAFU Climax!',
staleMediaIds: [],
},
],
}),
'utf8',
);
assert.equal(
await runtime.resolvePinnedMediaId({
mediaPath: seasonThreePath,
mediaTitle: null,
guess,
}),
108489,
);
// The season 1 file in the same folder must not inherit the season 3 override.
assert.equal(
await runtime.resolvePinnedMediaId({
mediaPath: '/anime/Oregairu/My Teen Romantic Comedy SNAFU (2013) - S01E01.mkv',
mediaTitle: null,
guess: { ...guess, season: 1 },
}),
null,
);
});