Files
kurihada 87edd6e35b refactor(core): 移除 BASH_TOOL_EXTRA_NOTES 和 GIT_COMMIT_AND_PR_CREATION_INSTRUCTION
- 从模板引擎中移除这两个函数的注册
- 从 bash 工具描述中移除对应的模板变量
- 删除不再需要的 git-instructions.txt 文件
- 更新单元测试,移除相关测试用例
2025-12-16 17:00:13 +08:00

244 lines
8.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
renderTemplate,
renderPromptTemplate,
createToolDescriptionContext,
} from '../../../src/template/renderer.js';
import type { ExtendedTemplateContext } from '../../../src/template/types.js';
import {
CUSTOM_TIMEOUT_MS,
MAX_TIMEOUT_MS,
MAX_OUTPUT_CHARS,
} from '../../../src/constants/bash-tool.js';
describe('Template Renderer - 模板渲染器', () => {
describe('基础变量替换', () => {
it('替换简单变量', () => {
const result = renderTemplate('Hello ${name}!', { name: 'World' });
expect(result).toBe('Hello World!');
});
it('替换嵌套属性', () => {
const result = renderTemplate('${user.name} is ${user.age}', {
user: { name: 'Alice', age: 30 },
});
expect(result).toBe('Alice is 30');
});
it('处理未定义变量 - 默认空字符串', () => {
const result = renderTemplate('Value: ${undefined_var}', {});
expect(result).toBe('Value: ');
});
it('处理未定义变量 - 自定义默认值', () => {
const result = renderTemplate('Value: ${undefined_var}', {}, { undefinedValue: 'N/A' });
expect(result).toBe('Value: N/A');
});
});
describe('三元表达式', () => {
it('条件为真时返回 trueValue', () => {
const result = renderTemplate('${isActive ? "Active" : "Inactive"}', { isActive: true });
expect(result).toBe('Active');
});
it('条件为假时返回 falseValue', () => {
const result = renderTemplate('${isActive ? "Active" : "Inactive"}', { isActive: false });
expect(result).toBe('Inactive');
});
it('空字符串被视为假', () => {
const result = renderTemplate('${value ? "yes" : "no"}', { value: '' });
expect(result).toBe('no');
});
});
describe('函数调用', () => {
it('调用注册的函数', () => {
const context: ExtendedTemplateContext = {
__functions__: {
GET_VALUE: () => 42,
},
};
const result = renderTemplate('Value: ${GET_VALUE()}', context);
expect(result).toBe('Value: 42');
});
it('调用返回字符串的函数', () => {
const context: ExtendedTemplateContext = {
__functions__: {
GET_MESSAGE: () => 'Hello from function',
},
};
const result = renderTemplate('${GET_MESSAGE()}', context);
expect(result).toBe('Hello from function');
});
it('未注册的函数返回空字符串', () => {
const context: ExtendedTemplateContext = {
__functions__: {},
};
const result = renderTemplate('Value: ${UNKNOWN_FUNC()}', context);
expect(result).toBe('Value: ');
});
it('没有 __functions__ 时返回空字符串', () => {
const result = renderTemplate('Value: ${SOME_FUNC()}', {});
expect(result).toBe('Value: ');
});
});
describe('算术运算(除法)', () => {
it('数值除法', () => {
const result = renderTemplate('${value / 1000}', { value: 60000 });
expect(result).toBe('60');
});
it('函数调用结果除法', () => {
const context: ExtendedTemplateContext = {
__functions__: {
GET_MS: () => 120000,
},
};
const result = renderTemplate('${GET_MS() / 60000} minutes', context);
expect(result).toBe('2 minutes');
});
it('结果向下取整', () => {
const result = renderTemplate('${value / 1000}', { value: 12345 });
expect(result).toBe('12');
});
it('除以零不进行除法运算', () => {
// 除以零时,divMatch 匹配但 divisor === 0,会跳过除法逻辑
// 然后尝试作为变量处理 "value / 0",找不到返回空
const result = renderTemplate('${value / 0}', { value: 100 });
expect(result).toBe('');
});
});
describe('组合表达式', () => {
it('函数调用 + 除法 + 字符串', () => {
const context: ExtendedTemplateContext = {
__functions__: {
TIMEOUT_MS: () => 600000,
},
};
const result = renderTemplate(
'Timeout: ${TIMEOUT_MS()}ms (${TIMEOUT_MS()/60000} minutes)',
context
);
expect(result).toBe('Timeout: 600000ms (10 minutes)');
});
it('多个函数调用', () => {
const context: ExtendedTemplateContext = {
__functions__: {
MIN_VAL: () => 10,
MAX_VAL: () => 100,
},
};
const result = renderTemplate('Range: ${MIN_VAL()} - ${MAX_VAL()}', context);
expect(result).toBe('Range: 10 - 100');
});
it('函数调用 + 变量混合', () => {
const context: ExtendedTemplateContext = {
toolName: 'bash',
__functions__: {
GET_TIMEOUT: () => 120000,
},
};
const result = renderTemplate(
'Use ${toolName} with timeout ${GET_TIMEOUT()}ms',
context
);
expect(result).toBe('Use bash with timeout 120000ms');
});
});
});
describe('createToolDescriptionContext - 工具描述上下文', () => {
it('包含工具名称变量', () => {
const context = createToolDescriptionContext();
expect(context.custom?.BASH_TOOL_NAME).toBe('bash');
expect(context.custom?.GREP_TOOL_NAME).toBe('grep');
expect(context.custom?.GLOB_TOOL_NAME).toBe('glob');
});
it('包含注册的模板函数', () => {
const context = createToolDescriptionContext();
expect(context.__functions__).toBeDefined();
expect(typeof context.__functions__.CUSTOM_TIMEOUT_MS).toBe('function');
expect(typeof context.__functions__.MAX_TIMEOUT_MS).toBe('function');
expect(typeof context.__functions__.MAX_OUTPUT_CHARS).toBe('function');
});
it('函数返回正确的配置值', () => {
const context = createToolDescriptionContext();
expect(context.__functions__.CUSTOM_TIMEOUT_MS()).toBe(CUSTOM_TIMEOUT_MS);
expect(context.__functions__.MAX_TIMEOUT_MS()).toBe(MAX_TIMEOUT_MS);
expect(context.__functions__.MAX_OUTPUT_CHARS()).toBe(MAX_OUTPUT_CHARS);
});
});
describe('renderPromptTemplate - 提示词模板渲染', () => {
it('渲染带函数调用的模板', () => {
const context = createToolDescriptionContext();
const template = 'Timeout: ${CUSTOM_TIMEOUT_MS()}ms (${CUSTOM_TIMEOUT_MS()/60000} minutes)';
const result = renderPromptTemplate(template, context);
expect(result).toBe(`Timeout: ${CUSTOM_TIMEOUT_MS}ms (${Math.floor(CUSTOM_TIMEOUT_MS / 60000)} minutes)`);
});
it('渲染带工具名变量的模板', () => {
const context = createToolDescriptionContext();
const template = 'Use ${BASH_TOOL_NAME} or ${GREP_TOOL_NAME}';
const result = renderPromptTemplate(template, context);
expect(result).toBe('Use bash or grep');
});
it('渲染完整的 bash 描述模板片段', () => {
const context = createToolDescriptionContext();
const template = `Commands timeout after \${MAX_TIMEOUT_MS()}ms (\${MAX_TIMEOUT_MS()/60000} minutes).
Output truncated at \${MAX_OUTPUT_CHARS()} chars.
Use \${GREP_TOOL_NAME} for search.`;
const result = renderPromptTemplate(template, context);
expect(result).toContain(`${MAX_TIMEOUT_MS}ms`);
expect(result).toContain(`${Math.floor(MAX_TIMEOUT_MS / 60000)} minutes`);
expect(result).toContain(`${MAX_OUTPUT_CHARS} chars`);
expect(result).toContain('grep for search');
});
});
describe('回归测试 - 确保现有功能正常', () => {
it('字符串字面量中的变量插值', () => {
const result = renderTemplate('${cond ? "Value is ${val}" : "No value"}', {
cond: true,
val: 42,
});
expect(result).toBe('Value is 42');
});
it('布尔值处理', () => {
expect(renderTemplate('${flag}', { flag: true })).toBe('true');
expect(renderTemplate('${flag}', { flag: false })).toBe('');
});
it('数字值处理', () => {
expect(renderTemplate('${num}', { num: 123 })).toBe('123');
expect(renderTemplate('${num}', { num: 0 })).toBe('0');
});
it('嵌套对象访问', () => {
const result = renderTemplate('${a.b.c}', { a: { b: { c: 'deep' } } });
expect(result).toBe('deep');
});
it('多个模板变量', () => {
const result = renderTemplate('${a} + ${b} = ${c}', { a: 1, b: 2, c: 3 });
expect(result).toBe('1 + 2 = 3');
});
});