96 lines
6.4 KiB
JavaScript
96 lines
6.4 KiB
JavaScript
'use strict';
|
||
|
||
const assert = require('node:assert/strict');
|
||
const fs = require('node:fs');
|
||
const os = require('node:os');
|
||
const path = require('node:path');
|
||
const { performance } = require('node:perf_hooks');
|
||
const { createSessionHistoryStore } = require('../lib/session-history-store');
|
||
const source = fs.readFileSync(path.join(__dirname, '..', 'server.js'), 'utf8');
|
||
function extract(name) {
|
||
const start = source.indexOf(`function ${name}(`);
|
||
assert(start >= 0, `缺少生产函数 ${name}`);
|
||
const end = source.indexOf('\nfunction ', start + 1);
|
||
return source.slice(start, end < 0 ? undefined : end);
|
||
}
|
||
const keys = source.slice(source.indexOf('const SESSION_META_PREVIEW_TOP_LEVEL_KEYS ='), source.indexOf('\nfunction isJsonWhitespace('));
|
||
const names = ['isJsonWhitespace', 'skipJsonWhitespace', 'readJsonStringEnd', 'readJsonContainerEnd', 'readJsonValueEnd',
|
||
'parseJsonTopLevelPrefixFields', 'skipJsonWhitespaceBackward', 'readJsonStringStart', 'readJsonValueStart',
|
||
'parseJsonTopLevelSuffixFields', 'readSessionPreviewTopLevelFields', 'previewStringField', 'previewBooleanField',
|
||
'previewCreatedFromKind', 'normalizeCreatedFromSourceSessionId', 'readSessionPreview', 'loadSessionMetaFromFile'];
|
||
let parses = 0;
|
||
const json = { parse(value) { parses += 1; return JSON.parse(value); } };
|
||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ccweb-session-metadata-'));
|
||
const observations = [];
|
||
const trackedFs = new Proxy(fs, { get(target, name) {
|
||
if (!['readFileSync', 'readSync', 'writeFileSync', 'renameSync'].includes(name)) return target[name];
|
||
return (...args) => { observations.push({ name, file: String(args[0]) }); return target[name](...args); };
|
||
} });
|
||
const store = createSessionHistoryStore({ sessionsDir: root, fs: trackedFs });
|
||
const api = new Function('fs', 'path', 'sessionHistoryStore', 'JSON', `
|
||
const SESSION_META_FULL_PARSE_MAX_BYTES = 65536;
|
||
const SESSION_META_PREVIEW_BYTES = 131072;
|
||
const SESSION_LOAD_MAX_BYTES = 33554432;
|
||
function normalizeSession(value) { if (value.messages.length) throw Error('列表不应规范化历史正文'); return value; }
|
||
function normalizeAgent(value) { return value || 'claude'; }
|
||
function getSessionAgent(value) { return normalizeAgent(value.agent); }
|
||
function normalizeGiteaSource(value) { return value || null; }
|
||
function sessionCreatedFromKind(value) { return value.createdFrom?.kind; }
|
||
function sanitizeId(value) { return /^[a-zA-Z0-9_-]+$/.test(value) ? value : ''; }
|
||
function isCodexThreadGoalActive(value) { return value?.status === 'active'; }
|
||
function plog() {}
|
||
${keys}
|
||
${names.map(extract).join('\n')}
|
||
return { suffix: parseJsonTopLevelSuffixFields, preview: readSessionPreviewTopLevelFields, meta: loadSessionMetaFromFile };
|
||
`)(trackedFs, path, store, json);
|
||
|
||
const fields = { id: '真实会话', title: '带"引号"及\\反斜杠', cwd: '/home/项目', hasUnread: false,
|
||
createdFrom: { kind: 'mcp', sourceSessionId: 'parent', nested: { title: '不能混入顶层' } },
|
||
giteaSource: { title: '来源', number: 2 }, pinnedAt: null };
|
||
const nested = { id: '嵌套ID', title: '工具标题', cwd: '/错误目录', input: '复杂字符串\\\\\"尾部,}]' };
|
||
const doc = JSON.stringify({ messages: Array.from({ length: 8000 }, () => nested), ...fields });
|
||
const tail = doc.slice(-131072);
|
||
parses = 0;
|
||
const start = performance.now();
|
||
assert.deepEqual(api.suffix(tail), fields);
|
||
assert(parses < 40, `尾部解析不应随嵌套伪字段增加而重复调用JSON.parse,实际${parses}`);
|
||
console.log(`通过:128KB高密度嵌套伪字段只解析${parses}次,耗时${Math.round(performance.now() - start)}ms`);
|
||
const fieldsStart = doc.lastIndexOf(',"id":"真实会话"') + 1;
|
||
for (let cut = 0; cut < fieldsStart; cut += 1031) assert.deepEqual(api.suffix(doc.slice(cut)), fields);
|
||
assert.deepEqual(api.suffix('{"title":"旧","title":"新","messages":[]}'), { title: '新' });
|
||
assert.deepEqual(api.suffix(JSON.stringify({ messages: [nested] }).slice(6)), {});
|
||
assert.equal(api.preview({ head: '{"id":"boundary","cwd":', tail: '"/不能拼接","messages":[]}' }).cwd, undefined);
|
||
for (const value of ['尾部\\', '尾部\\\\', '引号"', '\\"\\\\"\n', '转义括号}]\\"']) {
|
||
const text = JSON.stringify({ messages: [nested], title: value, createdFrom: { kind: value } });
|
||
assert.deepEqual(api.suffix(text), { title: value, createdFrom: { kind: value } });
|
||
}
|
||
console.log('通过:截断边界、转义引号、重复键、嵌套键和对象元数据');
|
||
|
||
const id = 'archive-only';
|
||
store.save({ id, title: '归档最新标题', agent: 'codexapp', cwd: '/home/真实项目', hasUnread: true,
|
||
codexAppGoal: { status: 'active' }, messages: [{ id: 'message', role: 'assistant', content: '正文'.repeat(1000000) }] }, { complete: true });
|
||
observations.length = 0;
|
||
assert.equal(api.meta(path.join(root, `${id}.json`)).title, '归档最新标题');
|
||
assert(!fs.existsSync(path.join(root, `${id}.json`)), '列举归档不应生成预览快照');
|
||
fs.writeFileSync(path.join(root, `${id}.json`), '{这是陈旧且损坏的缓存');
|
||
const meta = api.meta(path.join(root, `${id}.json`));
|
||
assert.equal(meta.title, '归档最新标题');
|
||
assert.equal(meta.hasUnread, true);
|
||
assert.equal(meta.codexAppGoalActive, true);
|
||
const manifestReads = observations.filter(item => item.name === 'readFileSync' && /manifest\.json$/.test(item.file)).length;
|
||
api.meta(path.join(root, `${id}.json`));
|
||
assert.equal(observations.filter(item => item.name === 'readFileSync' && /manifest\.json$/.test(item.file)).length, manifestReads,
|
||
'修订未变时列表不能反复解析归档清单');
|
||
assert(!observations.some(item => /chunks/.test(item.file)), '列表不得读取任何正文块');
|
||
assert(!observations.some(item => /write|rename/.test(item.name)), '列表不得重建快照或写文件');
|
||
assert(!observations.some(item => item.name === 'readFileSync' && item.file === path.join(root, `${id}.json`)), '有归档时不得解析快照');
|
||
const latest = store.loadMetadata(id);
|
||
latest.title = '修订后的标题';
|
||
latest.hasUnread = false;
|
||
store.save(latest);
|
||
assert.equal(api.meta(path.join(root, `${id}.json`)).title, '修订后的标题');
|
||
assert.equal(api.meta(path.join(root, `${id}.json`)).hasUnread, false);
|
||
fs.writeFileSync(path.join(root, 'legacy.json'), JSON.stringify({ id: 'legacy', messages: [nested], padding: 'x'.repeat(140000), ...fields }));
|
||
assert.equal(api.meta(path.join(root, 'legacy.json')).title, fields.title);
|
||
console.log('通过:归档列表不读正文、不写快照,缺失/损坏快照与旧格式均可列举');
|