Files
ai-terminal-assistant/tests/unit/utils/config.test.ts
T
kurihada 729fb2d42a feat: 添加完整的单元测试套件
- 新增 vitest 测试框架配置
- 添加 54 个测试文件,共 951 个测试用例
- 覆盖核心模块:
  - Agent: executor, registry, config-loader, permission-merger
  - Context: manager, compaction, prune, token-counter
  - Permission: manager, bash/file/git/web checkers, wildcard
  - Session: manager, storage
  - Tools: filesystem (12个), git (10个), web, shell, todo, task
  - LSP: client, server, language
  - Utils: config, diff
  - UI: terminal
2025-12-11 14:45:24 +08:00

196 lines
5.7 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
// Mock fs
vi.mock('fs', () => ({
existsSync: vi.fn(),
readFileSync: vi.fn(),
writeFileSync: vi.fn(),
mkdirSync: vi.fn(),
}));
import * as fs from 'fs';
import { getConfig, loadConfig, saveConfig } from '../../../src/utils/config.js';
describe('Config - 配置管理', () => {
const originalEnv = { ...process.env };
beforeEach(() => {
vi.clearAllMocks();
// 清理环境变量
delete process.env.AI_PROVIDER;
delete process.env.ANTHROPIC_API_KEY;
delete process.env.DEEPSEEK_API_KEY;
delete process.env.AI_MODEL;
delete process.env.AI_MAX_TOKENS;
});
afterEach(() => {
// 恢复环境变量
process.env = { ...originalEnv };
});
describe('getConfig - 获取原始配置', () => {
it('配置文件存在时返回内容', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
provider: 'anthropic',
apiKey: 'test-key',
model: 'claude-3-opus',
}));
const config = getConfig();
expect(config.provider).toBe('anthropic');
expect(config.apiKey).toBe('test-key');
expect(config.model).toBe('claude-3-opus');
});
it('配置文件不存在时返回空对象', () => {
vi.mocked(fs.existsSync).mockReturnValue(false);
const config = getConfig();
expect(config).toEqual({});
});
it('配置文件解析错误时返回空对象', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue('invalid json');
const config = getConfig();
expect(config).toEqual({});
});
});
describe('loadConfig - 加载完整配置', () => {
it('从环境变量获取 Anthropic 配置', () => {
process.env.ANTHROPIC_API_KEY = 'env-anthropic-key';
vi.mocked(fs.existsSync).mockReturnValue(false);
const config = loadConfig();
expect(config.provider).toBe('anthropic');
expect(config.apiKey).toBe('env-anthropic-key');
expect(config.model).toBe('claude-sonnet-4-20250514');
});
it('从环境变量获取 DeepSeek 配置', () => {
process.env.AI_PROVIDER = 'deepseek';
process.env.DEEPSEEK_API_KEY = 'env-deepseek-key';
vi.mocked(fs.existsSync).mockReturnValue(false);
const config = loadConfig();
expect(config.provider).toBe('deepseek');
expect(config.apiKey).toBe('env-deepseek-key');
expect(config.model).toBe('deepseek-chat');
});
it('配置文件优先级高于默认值', () => {
process.env.ANTHROPIC_API_KEY = 'env-key';
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
model: 'custom-model',
maxTokens: 8192,
}));
const config = loadConfig();
expect(config.model).toBe('custom-model');
expect(config.maxTokens).toBe(8192);
});
it('配置文件中的 provider 优先', () => {
process.env.ANTHROPIC_API_KEY = 'anthropic-key';
process.env.DEEPSEEK_API_KEY = 'deepseek-key';
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
provider: 'deepseek',
deepseekApiKey: 'stored-deepseek-key',
}));
const config = loadConfig();
expect(config.provider).toBe('deepseek');
// 使用环境变量中的 API Key(优先级更高)
expect(config.apiKey).toBe('deepseek-key');
});
it('包含系统提示词', () => {
process.env.ANTHROPIC_API_KEY = 'test-key';
vi.mocked(fs.existsSync).mockReturnValue(false);
const config = loadConfig();
expect(config.systemPrompt).toBeDefined();
expect(config.systemPrompt).toContain('终端');
});
it('默认 maxTokens 为 4096', () => {
process.env.ANTHROPIC_API_KEY = 'test-key';
vi.mocked(fs.existsSync).mockReturnValue(false);
const config = loadConfig();
expect(config.maxTokens).toBe(4096);
});
it('环境变量设置的 maxTokens', () => {
process.env.ANTHROPIC_API_KEY = 'test-key';
process.env.AI_MAX_TOKENS = '16384';
vi.mocked(fs.existsSync).mockReturnValue(false);
const config = loadConfig();
expect(config.maxTokens).toBe(16384);
});
});
describe('saveConfig - 保存配置', () => {
it('创建目录并保存配置', () => {
vi.mocked(fs.existsSync).mockReturnValue(false);
saveConfig({ provider: 'anthropic', apiKey: 'new-key' });
expect(fs.mkdirSync).toHaveBeenCalledWith(
expect.any(String),
{ recursive: true }
);
expect(fs.writeFileSync).toHaveBeenCalledWith(
expect.any(String),
expect.stringContaining('anthropic')
);
});
it('合并现有配置', () => {
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
provider: 'anthropic',
apiKey: 'old-key',
model: 'old-model',
}));
saveConfig({ apiKey: 'new-key' });
const writeCall = vi.mocked(fs.writeFileSync).mock.calls[0];
const savedConfig = JSON.parse(writeCall[1] as string);
expect(savedConfig.provider).toBe('anthropic'); // 保留
expect(savedConfig.apiKey).toBe('new-key'); // 更新
expect(savedConfig.model).toBe('old-model'); // 保留
});
it('目录已存在时不重新创建', () => {
vi.mocked(fs.existsSync)
.mockReturnValueOnce(true) // 目录存在
.mockReturnValueOnce(true); // 配置文件存在
vi.mocked(fs.readFileSync).mockReturnValue('{}');
saveConfig({ apiKey: 'test' });
expect(fs.mkdirSync).not.toHaveBeenCalled();
});
});
});