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);