修复: - main.ts 退出释放模型显存改用 getSetting(serverUrl),不再硬编码 127.0.0.1:11434(避免非默认地址时释放请求打到错误端口) - 备份导出/导入并入 localStorage 持久化状态(会话摘要、度量历史、轨迹降级缓存、主题),版本升级到 v2,实现完整备份 - 工具数量改为 getEnabledToolDefinitions().length 动态计算,删除写死"32 个"的硬编码 - 记忆日志区分操作来源:memory:write 透传 reason,标注"新增记忆/替换/删除/清空/TTL 衰减清理/访问统计写回(无新条目)",避免"写了但看不到新记忆"的困惑 可维护性: - 上下文压力逻辑收敛到统一 calculateContextStats,删除 getContextPressureLevel / getTrendAwareCompressThreshold 的重复实现 - 消除 validateToolArgs 同名碰撞(agent-engine 本地版改名 validateToolArgsQuick) - 子代理工具集改用 getEnabledToolDefinitions() 基线,跟随全局启用开关与 Plan 模式 - 抽取 html-utils.ts 纯函数模块(实体解码/HTML→文本/HTML→Markdown/拦截页检测/相关性评分),tool-handlers-system 净减约 190 行重复代码 - 统一静态导入(savePlanTracker/setPlanModeActive/collectDiagnostics/addWrittenFile) - console.* 使用处补充豁免说明(启动/退出/刷盘阶段无渲染进程可推送日志) - run_command 工具描述改为反映可配置执行模式 测试: - 新增 7 个测试文件 + 扩展 2 个,共 273 个测试(原 34 → 273) - 覆盖 agent-engine / agent-safety / context-manager / tool-registry / result-formatter / tool-parsing / memory-service / crypto / build-context / html-utils / utils / tool-handlers-fs - 全部通过 npm run typecheck && npm test && npm run build
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
generateId,
|
||||
formatTime,
|
||||
truncate,
|
||||
formatSize,
|
||||
escapeHtml,
|
||||
detectLanguage,
|
||||
} from '../src/renderer/utils/utils.js';
|
||||
|
||||
describe('generateId', () => {
|
||||
it('生成唯一 ID', () => {
|
||||
const a = generateId();
|
||||
const b = generateId();
|
||||
expect(a).not.toBe(b);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatTime', () => {
|
||||
it('格式化为 YYYY-MM-DD HH:MM:SS', () => {
|
||||
const ts = new Date(2026, 7, 26, 14, 30, 5).getTime();
|
||||
const out = formatTime(ts);
|
||||
expect(out).toMatch(/^2026-08-26 14:30:05$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('truncate', () => {
|
||||
it('短文本原样返回', () => {
|
||||
expect(truncate('hello', 10)).toBe('hello');
|
||||
});
|
||||
|
||||
it('超长文本截断加省略号', () => {
|
||||
expect(truncate('x'.repeat(20), 5)).toBe('xxxxx...');
|
||||
});
|
||||
|
||||
it('空字符串返回空', () => {
|
||||
expect(truncate('')).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatSize', () => {
|
||||
it('字节格式化到适当单位', () => {
|
||||
expect(formatSize(0)).toBe('');
|
||||
expect(formatSize(512)).toBe('512.0 B');
|
||||
expect(formatSize(1024)).toBe('1.0 KB');
|
||||
expect(formatSize(1024 * 1024)).toBe('1.0 MB');
|
||||
expect(formatSize(1024 * 1024 * 1024)).toBe('1.0 GB');
|
||||
});
|
||||
|
||||
it('大数值进位到 TB', () => {
|
||||
expect(formatSize(1024 ** 4)).toBe('1.0 TB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('escapeHtml', () => {
|
||||
it('转义 HTML 特殊字符', () => {
|
||||
expect(escapeHtml('<script>alert("x")</script>')).toBe('<script>alert("x")</script>');
|
||||
});
|
||||
|
||||
it('转义单引号与 &', () => {
|
||||
expect(escapeHtml("a'b & c")).toBe('a'b & c');
|
||||
});
|
||||
|
||||
it('null/undefined 返回空串', () => {
|
||||
expect(escapeHtml(null)).toBe('');
|
||||
expect(escapeHtml(undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('数字值被字符串化并转义', () => {
|
||||
expect(escapeHtml(42)).toBe('42');
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectLanguage', () => {
|
||||
it('常见扩展名识别', () => {
|
||||
expect(detectLanguage('main.ts')).toBe('typescript');
|
||||
expect(detectLanguage('app.py')).toBe('python');
|
||||
expect(detectLanguage('index.js')).toBe('javascript');
|
||||
expect(detectLanguage('style.css')).toBe('css');
|
||||
expect(detectLanguage('data.json')).toBe('json');
|
||||
});
|
||||
|
||||
it('特殊文件名识别', () => {
|
||||
expect(detectLanguage('Dockerfile')).toBe('dockerfile');
|
||||
expect(detectLanguage('Makefile')).toBe('makefile');
|
||||
});
|
||||
|
||||
it('未知扩展名返回自身', () => {
|
||||
// 有扩展名:未知映射返回扩展名本身
|
||||
expect(detectLanguage('file.xyz')).toBe('xyz');
|
||||
// 无扩展名:split 后 pop 得到整个文件名,未命中映射返回原值
|
||||
expect(detectLanguage('noext')).toBe('noext');
|
||||
});
|
||||
|
||||
it('大小写不敏感', () => {
|
||||
expect(detectLanguage('MAIN.TS')).toBe('typescript');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user