96 lines
3.4 KiB
JavaScript
96 lines
3.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
const {
|
||
parseCustomApiError,
|
||
} = await import('../src/lib/custom-api-fetch.ts');
|
||
const {
|
||
buildSynchronousImageRequestBody,
|
||
getSystemPollingFailureMessage,
|
||
shouldRetryImageRequestWithoutStream,
|
||
STREAM_UNSUPPORTED_SYNC_CONFIRM_PREFIX,
|
||
} = await import('../src/lib/custom-image-fallback.ts');
|
||
|
||
const repoRoot = path.resolve(import.meta.dirname, '..');
|
||
|
||
async function runTest(name, fn) {
|
||
try {
|
||
await fn();
|
||
console.log(`PASS ${name}`);
|
||
} catch (error) {
|
||
console.error(`FAIL ${name}`);
|
||
console.error(error);
|
||
process.exitCode = 1;
|
||
}
|
||
}
|
||
|
||
function read(relativePath) {
|
||
return fs.readFileSync(path.join(repoRoot, relativePath), 'utf8');
|
||
}
|
||
|
||
await runTest('detects stream timeout confirmation errors for synchronous fallback', () => {
|
||
assert.equal(
|
||
shouldRetryImageRequestWithoutStream(
|
||
{ model: 'gpt-image-2', prompt: 'test', stream: true },
|
||
`${STREAM_UNSUPPORTED_SYNC_CONFIRM_PREFIX}上游流式生图没有持续返回数据`,
|
||
),
|
||
true,
|
||
);
|
||
assert.equal(
|
||
shouldRetryImageRequestWithoutStream(
|
||
{ model: 'gpt-image-2', prompt: 'test', stream: false },
|
||
`${STREAM_UNSUPPORTED_SYNC_CONFIRM_PREFIX}上游流式生图没有持续返回数据`,
|
||
),
|
||
false,
|
||
);
|
||
});
|
||
|
||
await runTest('builds a synchronous retry body without mutating the original request', () => {
|
||
const original = { model: 'gpt-image-2', prompt: 'test', n: 1, stream: true };
|
||
const next = buildSynchronousImageRequestBody(original);
|
||
|
||
assert.deepEqual(next, { model: 'gpt-image-2', prompt: 'test', n: 1, stream: false });
|
||
assert.equal(original.stream, true);
|
||
});
|
||
|
||
await runTest('system polling exposes actionable upstream errors instead of generic busy message', () => {
|
||
assert.equal(
|
||
getSystemPollingFailureMessage('上游 API 同步生图请求超时(Cloudflare 524)。请降低分辨率后重试。'),
|
||
'上游 API 同步生图请求超时(Cloudflare 524)。请降低分辨率后重试。',
|
||
);
|
||
assert.equal(
|
||
getSystemPollingFailureMessage(`${STREAM_UNSUPPORTED_SYNC_CONFIRM_PREFIX}上游流式生图没有持续返回数据`),
|
||
'上游流式生图没有持续返回数据',
|
||
);
|
||
assert.equal(
|
||
getSystemPollingFailureMessage(''),
|
||
'因使用人数较多,模型繁忙,请稍后再试',
|
||
);
|
||
});
|
||
|
||
await runTest('Cloudflare gateway errors are shown as concise retryable upstream messages', () => {
|
||
const message = parseCustomApiError(502, '<!DOCTYPE html><title>mozhevip.top | 502: Bad gateway</title>');
|
||
|
||
assert.equal(message.includes('<!DOCTYPE html>'), false);
|
||
assert.match(message, /上游网关/);
|
||
assert.match(message, /502/);
|
||
});
|
||
|
||
await runTest('text-to-image custom fetch enables one retry for 502 503 504 gateway failures', () => {
|
||
const source = read('src/app/api/generate/image/route.ts');
|
||
|
||
assert.match(
|
||
source,
|
||
/fetchWithRetry\(\s*endpoint,\s*\{ method: 'POST', headers: buildCustomApiHeaders\(apiKey\), body: JSON\.stringify\(requestBody\) \},\s*GENERATION_TIMEOUT,\s*1,\s*\)/s,
|
||
);
|
||
});
|
||
|
||
await runTest('multimodal 524 errors do not reuse image-generation timeout wording', () => {
|
||
const message = parseCustomApiError(524, '<!DOCTYPE html><title>mozhevip.top | 524: A timeout occurred</title>', 'multimodal');
|
||
|
||
assert.match(message, /多模态模型同步请求超时/);
|
||
assert.equal(message.includes('生图请求超时'), false);
|
||
});
|
||
|
||
if (process.exitCode) process.exit(process.exitCode);
|