// 测试环境设置
import { before, after, beforeEach, afterEach } from 'node:test';
import { temporaryDirectory } from 'tempy';
import fs from 'fs-extra';
import path from 'path';
// 全局测试配置
export const testConfig = {
tempDir: temporaryDirectory(),
testDocsDir: path.join(process.cwd(), 'tests', 'fixtures', 'documents'),
cacheDir: path.join(temporaryDirectory(), '.cache'),
outputDir: path.join(temporaryDirectory(), 'output')
};
// 全局测试状态
export const testState = {
server: null,
mockDocuments: new Map(),
tempFiles: []
};
// 测试前设置
before(async () => {
// 创建测试目录
await fs.ensureDir(testConfig.testDocsDir);
await fs.ensureDir(testConfig.cacheDir);
await fs.ensureDir(testConfig.outputDir);
// 设置环境变量
process.env.NODE_ENV = 'test';
process.env.CACHE_DIR = testConfig.cacheDir;
process.env.OUTPUT_DIR = testConfig.outputDir;
});
// 测试后清理
after(async () => {
// 清理临时文件
for (const tempFile of testState.tempFiles) {
try {
await fs.remove(tempFile);
} catch (error) {
console.warn(`清理临时文件失败: ${tempFile}`, error);
}
}
// 清理测试目录
try {
await fs.remove(path.dirname(testConfig.cacheDir));
await fs.remove(testConfig.outputDir);
} catch (error) {
console.warn('清理测试目录失败', error);
}
// 重置环境变量
delete process.env.NODE_ENV;
delete process.env.CACHE_DIR;
delete process.env.OUTPUT_DIR;
});
// 每个测试前的设置
beforeEach(async () => {
// 重置测试状态
testState.mockDocuments.clear();
});
// 每个测试后的清理
afterEach(async () => {
// 清理临时文件
for (const tempFile of testState.tempFiles) {
try {
await fs.remove(tempFile);
} catch (error) {
// 忽略清理错误
}
}
testState.tempFiles.length = 0;
});
// 测试工具函数
export const testUtils = {
// 创建临时文件
async createTempFile(filename, content) {
const filePath = path.join(testConfig.tempDir, filename);
await fs.writeFile(filePath, content);
testState.tempFiles.push(filePath);
return filePath;
},
// 创建模拟Word文档
async createMockDocx(filename, options = {}) {
const {
withTables = false,
withImages = false,
textContent = '这是一个测试文档内容'
} = options;
// 创建简单的docx结构
const docxContent = `
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>${textContent}</w:t>
</w:r>
</w:p>
${withTables ? `
<w:tbl>
<w:tr>
<w:tc>
<w:p>
<w:r>
<w:t>表格单元格1</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:r>
<w:t>表格单元格2</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</w:tbl>` : ''}
</w:body>
</w:document>
`;
const filePath = path.join(testConfig.testDocsDir, filename);
await fs.writeFile(filePath, docxContent);
testState.mockDocuments.set(filename, filePath);
return filePath;
},
// 模拟MCP请求
createMCPRequest(method, params = {}) {
return {
jsonrpc: '2.0',
id: Date.now() + Math.random(),
method,
params
};
},
// 模拟工具调用请求
createToolCallRequest(toolName, args = {}) {
return this.createMCPRequest('tools/call', {
name: toolName,
arguments: args
});
},
// 验证MCP响应格式
validateMCPResponse(response) {
if (!response || typeof response !== 'object') {
throw new Error('响应不是有效对象');
}
if (response.error) {
throw new Error(`MCP错误: ${response.error.message}`);
}
return true;
},
// 等待异步操作
async wait(ms = 100) {
return new Promise(resolve => setTimeout(resolve, ms));
},
// 生成随机字符串
randomString(length = 10) {
return Math.random().toString(36).substring(2, 2 + length);
}
};
// 导出常用的断言函数
export const assertions = {
// 断言工具调用成功
assertToolSuccess(result) {
if (!result || result.error) {
throw new Error(`工具调用失败: ${result?.error?.message || '未知错误'}`);
}
},
// 断言响应包含内容
assertContentExists(result, expectedContent) {
if (!result.content || !Array.isArray(result.content)) {
throw new Error('响应缺少content字段');
}
const textContent = result.content
.filter(item => item.type === 'text')
.map(item => item.text)
.join(' ');
if (!textContent.includes(expectedContent)) {
throw new Error(`响应内容中未找到期望的文本: ${expectedContent}`);
}
},
// 断言文件存在
assertFileExists(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error(`文件不存在: ${filePath}`);
}
},
// 断言缓存条目存在
async assertCacheExists(cacheManager, key) {
const cached = await cacheManager.get(key);
if (!cached) {
throw new Error(`缓存条目不存在: ${key}`);
}
}
};