Files
ai-terminal-assistant/tests/unit/tools/todo/todo-manager.test.ts
T
kurihada bca19b7741 test: 补充单元测试提升代码覆盖率
新增测试文件:
- agent/executor-extended.test.ts, presets/
- context/manager-extended.test.ts
- core/agent.test.ts, providers.test.ts
- lsp/cli.test.ts, client-extended.test.ts, index.test.ts
- permission/file-prompt.test.ts, prompt.test.ts
- skills/builtin/
- tools/filesystem/write_file-extended.test.ts
- tools/git/git_commit-extended.test.ts
- tools/load_description.test.ts
- tools/todo/todo-manager.test.ts
- tools/tool-search.test.ts
- types/
- utils/config-extended.test.ts, diff-extended.test.ts

修改现有测试:
- agent/manager.test.ts
- tools/skill/skill.test.ts
- utils/config.test.ts, diff.test.ts, image.test.ts
2025-12-11 20:37:03 +08:00

275 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { todoManager } from '../../../../src/tools/todo/todo-manager.js';
import type { SessionManager } from '../../../../src/session/index.js';
import type { Todo } from '../../../../src/session/types.js';
describe('TodoManager', () => {
let mockSessionManager: SessionManager;
let mockTodos: Todo[];
beforeEach(() => {
mockTodos = [];
mockSessionManager = {
getTodos: vi.fn(() => mockTodos),
setTodos: vi.fn(async (todos: Todo[]) => {
mockTodos = todos;
}),
} as unknown as SessionManager;
// 重置 todoManager 状态
todoManager.setSessionManager(mockSessionManager);
});
describe('setSessionManager', () => {
it('设置会话管理器后 isInitialized 返回 true', () => {
const freshManager = {
getTodos: vi.fn(() => []),
setTodos: vi.fn(),
} as unknown as SessionManager;
todoManager.setSessionManager(freshManager);
expect(todoManager.isInitialized()).toBe(true);
});
});
describe('getTodos', () => {
it('返回空数组当没有 todo', () => {
const todos = todoManager.getTodos();
expect(todos).toEqual([]);
});
it('返回现有的 todo 列表', () => {
mockTodos = [
{ id: '1', content: 'Task 1', status: 'pending', createdAt: '', updatedAt: '' },
{ id: '2', content: 'Task 2', status: 'completed', createdAt: '', updatedAt: '' },
];
const todos = todoManager.getTodos();
expect(todos).toHaveLength(2);
expect(todos[0].content).toBe('Task 1');
expect(todos[1].content).toBe('Task 2');
});
it('正确调用 sessionManager.getTodos', () => {
todoManager.getTodos();
expect(mockSessionManager.getTodos).toHaveBeenCalled();
});
});
describe('setTodos', () => {
it('更新 todo 列表', async () => {
const newTodos: Todo[] = [
{ id: '1', content: 'New Task', status: 'pending', createdAt: '', updatedAt: '' },
];
await todoManager.setTodos(newTodos);
expect(mockSessionManager.setTodos).toHaveBeenCalledWith(newTodos);
expect(mockTodos).toHaveLength(1);
});
it('可以设置空列表', async () => {
mockTodos = [
{ id: '1', content: 'Task', status: 'pending', createdAt: '', updatedAt: '' },
];
await todoManager.setTodos([]);
expect(mockTodos).toHaveLength(0);
});
});
describe('addTodo', () => {
it('添加新的 todo(默认状态 pending', async () => {
const todo = await todoManager.addTodo('新任务');
expect(todo.content).toBe('新任务');
expect(todo.status).toBe('pending');
expect(todo.id).toBeDefined();
expect(todo.createdAt).toBeDefined();
expect(todo.updatedAt).toBeDefined();
expect(mockTodos).toHaveLength(1);
});
it('添加指定状态的 todo', async () => {
const todo = await todoManager.addTodo('进行中的任务', 'in_progress');
expect(todo.content).toBe('进行中的任务');
expect(todo.status).toBe('in_progress');
});
it('添加已完成状态的 todo', async () => {
const todo = await todoManager.addTodo('已完成任务', 'completed');
expect(todo.status).toBe('completed');
});
it('生成唯一 ID', async () => {
const todo1 = await todoManager.addTodo('Task 1');
const todo2 = await todoManager.addTodo('Task 2');
expect(todo1.id).not.toBe(todo2.id);
});
it('追加到现有列表', async () => {
mockTodos = [
{ id: 'existing', content: 'Existing', status: 'pending', createdAt: '', updatedAt: '' },
];
await todoManager.addTodo('New Task');
expect(mockTodos).toHaveLength(2);
expect(mockTodos[0].content).toBe('Existing');
expect(mockTodos[1].content).toBe('New Task');
});
});
describe('updateTodoStatus', () => {
beforeEach(() => {
mockTodos = [
{ id: 'todo-1', content: 'Task 1', status: 'pending', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
{ id: 'todo-2', content: 'Task 2', status: 'pending', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
];
});
it('更新存在的 todo 状态', async () => {
const result = await todoManager.updateTodoStatus('todo-1', 'completed');
expect(result).toBe(true);
expect(mockTodos[0].status).toBe('completed');
});
it('更新 updatedAt 时间戳', async () => {
const originalUpdatedAt = mockTodos[0].updatedAt;
await todoManager.updateTodoStatus('todo-1', 'in_progress');
expect(mockTodos[0].updatedAt).not.toBe(originalUpdatedAt);
});
it('不存在的 todo 返回 false', async () => {
const result = await todoManager.updateTodoStatus('non-existent', 'completed');
expect(result).toBe(false);
});
it('可以更新为任意状态', async () => {
await todoManager.updateTodoStatus('todo-1', 'in_progress');
expect(mockTodos[0].status).toBe('in_progress');
await todoManager.updateTodoStatus('todo-1', 'completed');
expect(mockTodos[0].status).toBe('completed');
await todoManager.updateTodoStatus('todo-1', 'pending');
expect(mockTodos[0].status).toBe('pending');
});
});
describe('deleteTodo', () => {
beforeEach(() => {
mockTodos = [
{ id: 'todo-1', content: 'Task 1', status: 'pending', createdAt: '', updatedAt: '' },
{ id: 'todo-2', content: 'Task 2', status: 'completed', createdAt: '', updatedAt: '' },
{ id: 'todo-3', content: 'Task 3', status: 'in_progress', createdAt: '', updatedAt: '' },
];
});
it('删除存在的 todo', async () => {
const result = await todoManager.deleteTodo('todo-2');
expect(result).toBe(true);
expect(mockTodos).toHaveLength(2);
expect(mockTodos.find(t => t.id === 'todo-2')).toBeUndefined();
});
it('删除第一个 todo', async () => {
const result = await todoManager.deleteTodo('todo-1');
expect(result).toBe(true);
expect(mockTodos[0].id).toBe('todo-2');
});
it('删除最后一个 todo', async () => {
const result = await todoManager.deleteTodo('todo-3');
expect(result).toBe(true);
expect(mockTodos).toHaveLength(2);
});
it('不存在的 todo 返回 false', async () => {
const result = await todoManager.deleteTodo('non-existent');
expect(result).toBe(false);
expect(mockTodos).toHaveLength(3);
});
it('删除后其他 todo 保持不变', async () => {
await todoManager.deleteTodo('todo-2');
expect(mockTodos[0].content).toBe('Task 1');
expect(mockTodos[1].content).toBe('Task 3');
});
});
describe('clearTodos', () => {
it('清空所有 todo', async () => {
mockTodos = [
{ id: '1', content: 'Task 1', status: 'pending', createdAt: '', updatedAt: '' },
{ id: '2', content: 'Task 2', status: 'completed', createdAt: '', updatedAt: '' },
];
await todoManager.clearTodos();
expect(mockTodos).toHaveLength(0);
expect(mockSessionManager.setTodos).toHaveBeenCalledWith([]);
});
it('空列表时调用也不报错', async () => {
mockTodos = [];
await expect(todoManager.clearTodos()).resolves.not.toThrow();
expect(mockTodos).toHaveLength(0);
});
});
describe('isInitialized', () => {
it('初始化后返回 true', () => {
expect(todoManager.isInitialized()).toBe(true);
});
});
describe('边界情况', () => {
it('处理包含特殊字符的 content', async () => {
const specialContent = '任务: "测试" & <script>alert(1)</script>';
const todo = await todoManager.addTodo(specialContent);
expect(todo.content).toBe(specialContent);
});
it('处理非常长的 content', async () => {
const longContent = 'A'.repeat(10000);
const todo = await todoManager.addTodo(longContent);
expect(todo.content).toBe(longContent);
});
it('处理空字符串 content', async () => {
const todo = await todoManager.addTodo('');
expect(todo.content).toBe('');
});
it('连续操作保持数据一致性', async () => {
await todoManager.addTodo('Task 1');
await todoManager.addTodo('Task 2');
await todoManager.updateTodoStatus(mockTodos[0].id, 'completed');
await todoManager.deleteTodo(mockTodos[1].id);
await todoManager.addTodo('Task 3');
expect(mockTodos).toHaveLength(2);
expect(mockTodos[0].status).toBe('completed');
expect(mockTodos[1].content).toBe('Task 3');
});
});
});