5.9 KiB
5.9 KiB
Plan 14 — 集成测试搭建
优先级:中 | 新建 ~3 个测试文件 | 预估 ~30 个测试用例
当前 tests/integration/ 目录为空,spec 设计的三个集成测试均未创建。本计划搭建 mock 基础设施并实现核心集成测试。
14.1 搭建 tests/mocks/ 基础设施
文件结构
tests/
├── mocks/
│ ├── api-responses.ts # Claude API mock 响应
│ ├── file-system.ts # 临时文件系统工具
│ └── fixtures/
│ ├── sample-claudemd.md # CLAUDE.md 样本
│ └── sample-messages.json # 消息样本
├── integration/
│ ├── tool-chain.test.ts
│ ├── context-build.test.ts
│ └── message-pipeline.test.ts
└── helpers/
└── setup.ts # 共享 beforeAll/afterAll
tests/mocks/file-system.ts
import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
export async function createTempDir(prefix = "claude-test-"): Promise<string> {
const dir = await mkdtemp(join(tmpdir(), prefix));
return dir;
}
export async function cleanupTempDir(dir: string): Promise<void> {
await rm(dir, { recursive: true, force: true });
}
export async function writeTempFile(dir: string, name: string, content: string): Promise<string> {
const path = join(dir, name);
await writeFile(path, content, "utf-8");
return path;
}
tests/mocks/fixtures/sample-claudemd.md
# Project Instructions
This is a sample CLAUDE.md file for testing.
tests/mocks/api-responses.ts
export const mockStreamResponse = {
type: "message_start" as const,
message: {
id: "msg_mock_001",
type: "message" as const,
role: "assistant",
content: [],
model: "claude-sonnet-4-20250514",
stop_reason: null,
stop_sequence: null,
usage: { input_tokens: 100, output_tokens: 0 },
},
};
export const mockTextBlock = {
type: "content_block_start" as const,
index: 0,
content_block: { type: "text" as const, text: "Mock response" },
};
export const mockToolUseBlock = {
type: "content_block_start" as const,
index: 1,
content_block: {
type: "tool_use" as const,
id: "toolu_mock_001",
name: "Read",
input: { file_path: "/tmp/test.txt" },
},
};
export const mockEndEvent = {
type: "message_stop" as const,
};
14.2 tests/integration/tool-chain.test.ts
目标:验证 Tool 注册 → 发现 → 权限检查链路。
前置条件
src/tools.ts 的 getAllBaseTools / getTools 导入链过重。策略:
- 尝试直接 import 并 mock 最重依赖
- 若不可行,改为测试
src/Tool.ts的findToolByName+ 手动构造 tool 列表
用例
| # | 用例 | 验证点 |
|---|---|---|
| 1 | findToolByName("Bash") 在已注册列表中查找 |
返回正确的 tool 定义 |
| 2 | findToolByName("NonExistent") |
返回 undefined |
| 3 | findToolByName 大小写不敏感 |
"bash" 也能找到 |
| 4 | filterToolsByDenyRules 拒绝特定工具 |
被拒绝工具不在结果中 |
| 5 | parseToolPreset("default") 返回已知列表 |
包含核心 tools |
| 6 | buildTool 构建的 tool 可被 findToolByName 发现 |
端到端验证 |
如果
getAllBaseTools确实不可导入,改用 mock tool list 替代。
14.3 tests/integration/context-build.test.ts
目标:验证系统提示组装流程(CLAUDE.md 加载 + git status + 日期注入)。
前置条件
src/context.ts 依赖链极重。策略:
- Mock
src/bootstrap/state.ts(提供 cwd、projectRoot) - Mock
src/utils/git.ts(提供 git status) - 使用真实
src/utils/claudemd.ts+ 临时文件
用例
| # | 用例 | 验证点 |
|---|---|---|
| 1 | 基本 context 构建 | 返回值包含系统提示字符串 |
| 2 | CLAUDE.md 内容出现在 context 中 | stripHtmlComments 后的内容被包含 |
| 3 | 多层目录 CLAUDE.md 合并 | 父目录 + 子目录 CLAUDE.md 都被加载 |
| 4 | 无 CLAUDE.md 时不报错 | context 正常返回,无 crash |
| 5 | git status 为 null | context 正常构建(测试环境中 git 不可用时) |
风险评估:如果 mock
context.ts的依赖链成本过高,退化为测试buildEffectiveSystemPrompt(已在 systemPrompt.test.ts 中完成),记录为已知限制。
14.4 tests/integration/message-pipeline.test.ts
目标:验证用户输入 → 消息格式化 → API 请求构建。
前置条件
src/services/api/claude.ts 构建最终 API 请求。策略:
- Mock Anthropic SDK 的 streaming endpoint
- 验证请求参数结构
用例
| # | 用例 | 验证点 |
|---|---|---|
| 1 | 文本消息格式化 | createUserMessage 生成正确 role+content |
| 2 | tool_result 消息格式化 | 包含 tool_use_id 和 content |
| 3 | 多轮消息序列化 | messages 数组保持顺序 |
| 4 | 系统提示注入到请求 | API 请求的 system 字段非空 |
| 5 | 消息 normalize 后格式一致 | normalizeMessages 输出结构正确 |
现实评估:消息格式化的大部分已在
messages.test.ts覆盖。API 请求构建需要 mock SDK,复杂度高。如果投入产出比低,仅实现用例 1-3 和 5,用例 4 标记为 stretch goal。
实施步骤
- 创建
tests/mocks/目录和基础文件 - 实现
tool-chain.test.ts(最低风险,最高价值) - 评估
context-build.test.ts可行性,决定是否实施 - 实现
message-pipeline.test.ts(可降级为单元测试) - 更新
testing-spec.md状态
验收标准
tests/mocks/基础设施可用- 至少
tool-chain.test.ts实现并通过 - 集成测试独立于单元测试运行:
bun test tests/integration/ - 所有集成测试使用
createTempDir+cleanupTempDir,不留文件系统残留 bun test全部通过