File size: 4,123 Bytes
097fb32 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | /**
* test/unit-log-persist-default-summary.mjs
*
* 回归测试:未显式设置 LOG_PERSIST_MODE / logging.persist_mode 时,
* 默认落盘模式应为 summary。
* 运行方式:npm run build && node test/unit-log-persist-default-summary.mjs
*/
import fs from 'fs';
import path from 'path';
const LOG_DIR = '/tmp/cursor2api-log-default-summary';
process.env.LOG_FILE_ENABLED = '1';
process.env.LOG_DIR = LOG_DIR;
delete process.env.LOG_PERSIST_MODE;
const { handleOpenAIChatCompletions } = await import('../dist/openai-handler.js');
const { clearAllLogs } = await import('../dist/logger.js');
let passed = 0;
let failed = 0;
function assert(condition, msg) {
if (!condition) throw new Error(msg || 'Assertion failed');
}
function createCursorSseResponse(deltas) {
const encoder = new TextEncoder();
const stream = new ReadableStream({
start(controller) {
for (const delta of deltas) {
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'text-delta', delta })}\n\n`));
}
controller.close();
},
});
return new Response(stream, {
status: 200,
headers: { 'Content-Type': 'text/event-stream' },
});
}
class MockResponse {
constructor() {
this.statusCode = 200;
this.headers = {};
this.body = '';
this.ended = false;
}
writeHead(statusCode, headers) {
this.statusCode = statusCode;
this.headers = { ...this.headers, ...headers };
}
write(chunk) {
this.body += String(chunk);
return true;
}
end(chunk = '') {
this.body += String(chunk);
this.ended = true;
}
json(obj) {
this.writeHead(this.statusCode, { 'Content-Type': 'application/json' });
this.end(JSON.stringify(obj));
}
status(code) {
this.statusCode = code;
return this;
}
}
function resetLogs() {
clearAllLogs();
fs.rmSync(LOG_DIR, { recursive: true, force: true });
}
function latestPersistedRecord() {
const files = fs.readdirSync(LOG_DIR).filter(name => name.endsWith('.jsonl')).sort();
assert(files.length > 0, '应生成 JSONL 文件');
const file = path.join(LOG_DIR, files[files.length - 1]);
const lines = fs.readFileSync(file, 'utf8').split('\n').filter(Boolean);
assert(lines.length > 0, 'JSONL 不应为空');
return JSON.parse(lines[lines.length - 1]);
}
async function runTest(name, fn) {
try {
resetLogs();
await fn();
console.log(` ✅ ${name}`);
passed++;
} catch (e) {
console.error(` ❌ ${name}`);
console.error(` ${e.message}`);
failed++;
}
}
console.log('\n📦 [1] 默认落盘模式为 summary 回归\n');
await runTest('未显式配置 persist_mode 时默认只保留问答摘要', async () => {
const originalFetch = global.fetch;
global.fetch = async () => createCursorSseResponse(['Hello', ' world']);
try {
const req = {
method: 'POST',
path: '/v1/chat/completions',
body: {
model: 'gpt-4.1',
stream: true,
messages: [{ role: 'user', content: 'Please greet me briefly.' }],
},
};
const res = new MockResponse();
await handleOpenAIChatCompletions(req, res);
const persisted = latestPersistedRecord();
assert(persisted.payload.question.includes('Please greet me briefly.'), '默认模式应保留 question');
assert(persisted.payload.answer.includes('Hello world'), '默认模式应保留 answer');
assert(persisted.payload.finalResponse === undefined, '默认模式不应保留 finalResponse');
assert(persisted.payload.messages === undefined, '默认模式不应保留 messages');
} finally {
global.fetch = originalFetch;
}
});
console.log('\n' + '═'.repeat(55));
console.log(` 结果: ${passed} 通过 / ${failed} 失败 / ${passed + failed} 总计`);
console.log('═'.repeat(55) + '\n');
if (failed > 0) process.exit(1);
|