Files
cursor2api/test/unit-openai-image-file.mjs
小海 ed6181a5a9 fix: harden OpenAI multimodal compatibility and image handling
Tighten image path normalization, preserve multimodal request content across OpenAI-compatible endpoints, and fail fast on unsupported image_file inputs so clients get predictable behavior instead of silent degradation.

Made-with: Cursor
2026-03-17 15:03:39 +08:00

102 lines
3.3 KiB
JavaScript
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.
/**
* test/unit-openai-image-file.mjs
*
* 单元测试image_file 输入应显式报错,而不是静默降级
* 运行方式node test/unit-openai-image-file.mjs
*/
let passed = 0;
let failed = 0;
function test(name, fn) {
try {
fn();
console.log(`${name}`);
passed++;
} catch (e) {
console.error(`${name}`);
console.error(` ${e.message}`);
failed++;
}
}
function assert(condition, msg) {
if (!condition) throw new Error(msg || 'Assertion failed');
}
class OpenAIRequestError extends Error {
constructor(message, status = 400, type = 'invalid_request_error', code = 'invalid_request') {
super(message);
this.name = 'OpenAIRequestError';
this.status = status;
this.type = type;
this.code = code;
}
}
function unsupportedImageFileError(fileId) {
const suffix = fileId ? ` (file_id: ${fileId})` : '';
return new OpenAIRequestError(
`Unsupported content part: image_file${suffix}. This proxy does not support OpenAI Files API image references. Please send the image as image_url, input_image, data URI, or a local file path instead.`,
400,
'invalid_request_error',
'unsupported_content_part'
);
}
function extractOpenAIContentBlocks(msg) {
if (msg.content === null || msg.content === undefined) return '';
if (typeof msg.content === 'string') return msg.content;
if (Array.isArray(msg.content)) {
const blocks = [];
for (const p of msg.content) {
if (p.type === 'text' || p.type === 'input_text') {
if (p.text) blocks.push({ type: 'text', text: p.text });
} else if (p.type === 'image_file' && p.image_file) {
throw unsupportedImageFileError(p.image_file.file_id);
}
}
return blocks.length > 0 ? blocks : '';
}
return String(msg.content);
}
console.log('\n📦 [1] image_file 显式报错\n');
test('image_file 应抛出 OpenAIRequestError', () => {
let thrown;
try {
extractOpenAIContentBlocks({
role: 'user',
content: [
{ type: 'input_text', text: '请描述图片' },
{ type: 'image_file', image_file: { file_id: 'file_123' } },
],
});
} catch (e) {
thrown = e;
}
assert(thrown instanceof OpenAIRequestError, 'should throw OpenAIRequestError');
assert(thrown.message.includes('image_file'), 'message should mention image_file');
assert(thrown.message.includes('file_123'), 'message should include file_id');
assert(thrown.status === 400, 'status should be 400');
assert(thrown.type === 'invalid_request_error', 'type should be invalid_request_error');
assert(thrown.code === 'unsupported_content_part', 'code should be unsupported_content_part');
});
test('普通文本块仍可正常通过', () => {
const blocks = extractOpenAIContentBlocks({
role: 'user',
content: [{ type: 'input_text', text: 'hello' }],
});
assert(Array.isArray(blocks), 'blocks should be array');
assert(blocks[0].text === 'hello', 'text block should remain intact');
});
console.log('\n' + '═'.repeat(55));
console.log(` 结果: ${passed} 通过 / ${failed} 失败 / ${passed + failed} 总计`);
console.log('═'.repeat(55) + '\n');
if (failed > 0) process.exit(1);