mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
105 lines
3.1 KiB
TypeScript
105 lines
3.1 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import { withRetry } from '@/lib/api/http-utils';
|
|
import { probeSourceLatency } from '@/lib/api/source-latency';
|
|
import { processSearchStream } from '@/lib/utils/search-stream';
|
|
import { probeLatencyTargets } from '@/lib/utils/latency';
|
|
|
|
test('search stream does not complete during a slow but active source search', async () => {
|
|
const encoder = new TextEncoder();
|
|
const stream = new ReadableStream<Uint8Array>({
|
|
start(controller) {
|
|
controller.enqueue(encoder.encode('data: {"type":"start","totalSources":1}\n\n'));
|
|
|
|
setTimeout(() => {
|
|
controller.enqueue(encoder.encode(
|
|
'data: {"type":"videos","source":"slow","videos":[{"vod_id":1,"vod_name":"测试影片","source":"slow"}]}\n\n',
|
|
));
|
|
controller.enqueue(encoder.encode(
|
|
'data: {"type":"complete","totalVideosFound":1,"totalSources":1}\n\n',
|
|
));
|
|
controller.close();
|
|
}, 3200);
|
|
},
|
|
});
|
|
|
|
let completionCount = 0;
|
|
const receivedTitles: string[] = [];
|
|
|
|
await processSearchStream({
|
|
reader: stream.getReader(),
|
|
currentQuery: '测试',
|
|
onStart: () => {},
|
|
onVideos: (videos) => receivedTitles.push(...videos.map((video) => video.vod_name)),
|
|
onProgress: () => {},
|
|
onComplete: () => { completionCount += 1; },
|
|
onError: (message) => assert.fail(message),
|
|
});
|
|
|
|
assert.deepEqual(receivedTitles, ['测试影片']);
|
|
assert.equal(completionCount, 1);
|
|
});
|
|
|
|
test('GET fallback latency excludes the failed HEAD attempt duration', async () => {
|
|
const timestamps = [0, 5000, 5000, 5250];
|
|
const methods: string[] = [];
|
|
const fetcher: typeof fetch = async (_input, init) => {
|
|
methods.push(init?.method ?? 'GET');
|
|
if (init?.method === 'HEAD') {
|
|
throw new Error('HEAD unsupported');
|
|
}
|
|
return new Response(null, { status: 200 });
|
|
};
|
|
|
|
const result = await probeSourceLatency('https://example.com', {
|
|
fetcher,
|
|
now: () => timestamps.shift() ?? 5250,
|
|
});
|
|
|
|
assert.deepEqual(methods, ['HEAD', 'GET']);
|
|
assert.deepEqual(result, {
|
|
latency: 250,
|
|
success: true,
|
|
timeout: false,
|
|
method: 'GET',
|
|
});
|
|
});
|
|
|
|
test('latency probes cap concurrent outbound requests', async () => {
|
|
const targets = Array.from({ length: 10 }, (_, index) => ({
|
|
id: `source-${index}`,
|
|
baseUrl: `https://example.com/${index}`,
|
|
}));
|
|
let active = 0;
|
|
let maxActive = 0;
|
|
|
|
const results = await probeLatencyTargets(targets, async () => {
|
|
active += 1;
|
|
maxActive = Math.max(maxActive, active);
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
active -= 1;
|
|
return 42;
|
|
}, 3);
|
|
|
|
assert.equal(maxActive, 3);
|
|
assert.equal(results.length, targets.length);
|
|
assert.equal(results.every((result) => result.latency === 42), true);
|
|
});
|
|
|
|
test('aborted source requests are not retried', async () => {
|
|
let attempts = 0;
|
|
const abortError = new Error('aborted');
|
|
abortError.name = 'AbortError';
|
|
|
|
await assert.rejects(
|
|
withRetry(async () => {
|
|
attempts += 1;
|
|
throw abortError;
|
|
}),
|
|
{ name: 'AbortError' },
|
|
);
|
|
|
|
assert.equal(attempts, 1);
|
|
});
|