feat(stats): speed up session maintenance and improve stats UI (#111)

This commit is contained in:
2026-06-08 02:20:52 -07:00
committed by GitHub
parent e6a16a069b
commit 311f1e8ee5
108 changed files with 7441 additions and 729 deletions
+65
View File
@@ -32,6 +32,43 @@ test('resolveStatsBaseUrl keeps legacy localhost fallback for file mode without
assert.equal(baseUrl, 'http://127.0.0.1:6969');
});
test('getAnimeCoverUrl appends retry tokens for late cover refreshes', () => {
const getAnimeCoverUrl = apiClient.getAnimeCoverUrl as (
animeId: number,
retryToken?: number,
) => string;
assert.equal(
getAnimeCoverUrl(42, 3),
'http://127.0.0.1:6969/api/stats/anime/42/cover?coverRetry=3',
);
});
test('getCoverImages batches anime and media cover requests', async () => {
const originalFetch = globalThis.fetch;
let seenUrl = '';
let seenMethod = '';
let seenBody = '';
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
seenUrl = String(input);
seenMethod = init?.method ?? 'GET';
seenBody = String(init?.body ?? '');
return new Response(JSON.stringify({ anime: {}, media: {} }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof globalThis.fetch;
try {
await apiClient.getCoverImages({ animeIds: [1, 1, 2], videoIds: [7, 7, 8] });
assert.equal(seenUrl, `${BASE_URL}/api/stats/covers`);
assert.equal(seenMethod, 'POST');
assert.deepEqual(JSON.parse(seenBody), { animeIds: [1, 2], videoIds: [7, 8] });
} finally {
globalThis.fetch = originalFetch;
}
});
test('deleteSession sends a DELETE request to the session endpoint', async () => {
const originalFetch = globalThis.fetch;
let seenUrl = '';
@@ -51,6 +88,34 @@ test('deleteSession sends a DELETE request to the session endpoint', async () =>
}
});
test('searchSentences encodes realtime sentence search requests', async () => {
const originalFetch = globalThis.fetch;
let seenUrl = '';
globalThis.fetch = (async (input: RequestInfo | URL) => {
seenUrl = String(input);
return new Response(JSON.stringify([]), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}) as typeof globalThis.fetch;
try {
await apiClient.searchSentences('猫 食べる', 25);
assert.equal(
seenUrl,
`${BASE_URL}/api/stats/sentences/search?q=%E7%8C%AB+%E9%A3%9F%E3%81%B9%E3%82%8B&limit=25&headword=true`,
);
await apiClient.searchSentences('猫 食べる', 25, false);
assert.equal(
seenUrl,
`${BASE_URL}/api/stats/sentences/search?q=%E7%8C%AB+%E9%A3%9F%E3%81%B9%E3%82%8B&limit=25&headword=false`,
);
} finally {
globalThis.fetch = originalFetch;
}
});
test('deleteSession throws when the stats API delete request fails', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = (async () =>