import { describe, it, expect, beforeEach, vi } from 'vitest'; import type { Todo, TodoStatus } from '../../../src/session/types.js'; import type { SessionManager } from '../../../src/session/index.js'; // 需要单独导入 todoManager 因为它是单例 // 每个测试创建新的实例来避免状态污染 // 简单的 TodoManager 测试类(复制逻辑以测试) class TestTodoManager { private sessionManager: SessionManager | null = null; setSessionManager(manager: SessionManager): void { this.sessionManager = manager; } getTodos(): Todo[] { if (!this.sessionManager) { return []; } return this.sessionManager.getTodos(); } async setTodos(todos: Todo[]): Promise { if (!this.sessionManager) { return; } await this.sessionManager.setTodos(todos); } async addTodo(content: string, status: TodoStatus = 'pending'): Promise { const todos = this.getTodos(); const now = new Date().toISOString(); const newTodo: Todo = { id: this.generateId(), content, status, createdAt: now, updatedAt: now, }; todos.push(newTodo); await this.setTodos(todos); return newTodo; } async updateTodoStatus(id: string, status: TodoStatus): Promise { const todos = this.getTodos(); const todo = todos.find((t) => t.id === id); if (!todo) return false; todo.status = status; todo.updatedAt = new Date().toISOString(); await this.setTodos(todos); return true; } async deleteTodo(id: string): Promise { const todos = this.getTodos(); const index = todos.findIndex((t) => t.id === id); if (index === -1) return false; todos.splice(index, 1); await this.setTodos(todos); return true; } async clearTodos(): Promise { await this.setTodos([]); } private generateId(): string { return Math.random().toString(36).substring(2, 10); } isInitialized(): boolean { return this.sessionManager !== null; } } // Mock SessionManager function createMockSessionManager(): SessionManager { let todos: Todo[] = []; return { getTodos: vi.fn(() => [...todos]), setTodos: vi.fn(async (newTodos: Todo[]) => { todos = [...newTodos]; }), } as unknown as SessionManager; } describe('TodoManager - Todo 管理器', () => { let todoManager: TestTodoManager; let mockSessionManager: SessionManager; beforeEach(() => { todoManager = new TestTodoManager(); mockSessionManager = createMockSessionManager(); }); describe('初始化状态', () => { it('未设置 sessionManager 时 isInitialized 返回 false', () => { expect(todoManager.isInitialized()).toBe(false); }); it('设置 sessionManager 后 isInitialized 返回 true', () => { todoManager.setSessionManager(mockSessionManager); expect(todoManager.isInitialized()).toBe(true); }); it('未初始化时 getTodos 返回空数组', () => { expect(todoManager.getTodos()).toEqual([]); }); it('未初始化时 setTodos 不报错', async () => { await expect(todoManager.setTodos([{ id: '1', content: 'test', status: 'pending', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }])).resolves.not.toThrow(); }); }); describe('getTodos', () => { beforeEach(() => { todoManager.setSessionManager(mockSessionManager); }); it('返回空数组当没有 todos', () => { const todos = todoManager.getTodos(); expect(todos).toEqual([]); }); it('返回 sessionManager 中的 todos', async () => { await todoManager.addTodo('Task 1'); await todoManager.addTodo('Task 2'); const todos = todoManager.getTodos(); expect(todos).toHaveLength(2); }); }); describe('setTodos', () => { beforeEach(() => { todoManager.setSessionManager(mockSessionManager); }); it('设置 todos 列表', async () => { const todos: Todo[] = [ { id: '1', content: 'Task 1', status: 'pending', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }, ]; await todoManager.setTodos(todos); expect(todoManager.getTodos()).toHaveLength(1); expect(todoManager.getTodos()[0].content).toBe('Task 1'); }); it('清空 todos', async () => { await todoManager.addTodo('Task 1'); await todoManager.setTodos([]); expect(todoManager.getTodos()).toHaveLength(0); }); }); describe('addTodo', () => { beforeEach(() => { todoManager.setSessionManager(mockSessionManager); }); it('添加新 todo 默认状态为 pending', async () => { const todo = await todoManager.addTodo('New task'); expect(todo.content).toBe('New task'); expect(todo.status).toBe('pending'); expect(todo.id).toBeDefined(); expect(todo.createdAt).toBeDefined(); expect(todo.updatedAt).toBeDefined(); }); it('添加新 todo 指定状态', async () => { const todo = await todoManager.addTodo('In progress task', 'in_progress'); expect(todo.status).toBe('in_progress'); }); it('添加的 todo 出现在列表中', async () => { await todoManager.addTodo('Task 1'); await todoManager.addTodo('Task 2'); const todos = todoManager.getTodos(); expect(todos).toHaveLength(2); expect(todos.some(t => t.content === 'Task 1')).toBe(true); expect(todos.some(t => t.content === 'Task 2')).toBe(true); }); it('每个 todo 有唯一 ID', async () => { const todo1 = await todoManager.addTodo('Task 1'); const todo2 = await todoManager.addTodo('Task 2'); expect(todo1.id).not.toBe(todo2.id); }); }); describe('updateTodoStatus', () => { beforeEach(() => { todoManager.setSessionManager(mockSessionManager); }); it('更新存在的 todo 状态', async () => { const todo = await todoManager.addTodo('Task'); const result = await todoManager.updateTodoStatus(todo.id, 'completed'); expect(result).toBe(true); const updated = todoManager.getTodos().find(t => t.id === todo.id); expect(updated?.status).toBe('completed'); }); it('更新不存在的 todo 返回 false', async () => { const result = await todoManager.updateTodoStatus('non-existent-id', 'completed'); expect(result).toBe(false); }); it('更新状态时更新 updatedAt', async () => { const todo = await todoManager.addTodo('Task'); const originalUpdatedAt = todo.updatedAt; // 等待一小段时间确保时间戳不同 await new Promise(resolve => setTimeout(resolve, 10)); await todoManager.updateTodoStatus(todo.id, 'in_progress'); const updated = todoManager.getTodos().find(t => t.id === todo.id); expect(updated?.updatedAt).not.toBe(originalUpdatedAt); }); }); describe('deleteTodo', () => { beforeEach(() => { todoManager.setSessionManager(mockSessionManager); }); it('删除存在的 todo', async () => { const todo = await todoManager.addTodo('Task to delete'); const result = await todoManager.deleteTodo(todo.id); expect(result).toBe(true); expect(todoManager.getTodos().find(t => t.id === todo.id)).toBeUndefined(); }); it('删除不存在的 todo 返回 false', async () => { const result = await todoManager.deleteTodo('non-existent-id'); expect(result).toBe(false); }); it('删除后列表长度减少', async () => { await todoManager.addTodo('Task 1'); const todo2 = await todoManager.addTodo('Task 2'); await todoManager.addTodo('Task 3'); await todoManager.deleteTodo(todo2.id); expect(todoManager.getTodos()).toHaveLength(2); }); }); describe('clearTodos', () => { beforeEach(() => { todoManager.setSessionManager(mockSessionManager); }); it('清空所有 todos', async () => { await todoManager.addTodo('Task 1'); await todoManager.addTodo('Task 2'); await todoManager.addTodo('Task 3'); await todoManager.clearTodos(); expect(todoManager.getTodos()).toHaveLength(0); }); it('清空空列表不报错', async () => { await expect(todoManager.clearTodos()).resolves.not.toThrow(); }); }); }); describe('Todo 状态流转', () => { let todoManager: TestTodoManager; let mockSessionManager: SessionManager; beforeEach(() => { todoManager = new TestTodoManager(); mockSessionManager = createMockSessionManager(); todoManager.setSessionManager(mockSessionManager); }); it('pending -> in_progress -> completed', async () => { const todo = await todoManager.addTodo('Task'); expect(todo.status).toBe('pending'); await todoManager.updateTodoStatus(todo.id, 'in_progress'); let updated = todoManager.getTodos().find(t => t.id === todo.id); expect(updated?.status).toBe('in_progress'); await todoManager.updateTodoStatus(todo.id, 'completed'); updated = todoManager.getTodos().find(t => t.id === todo.id); expect(updated?.status).toBe('completed'); }); it('直接从 pending 到 completed', async () => { const todo = await todoManager.addTodo('Quick task'); await todoManager.updateTodoStatus(todo.id, 'completed'); const updated = todoManager.getTodos().find(t => t.id === todo.id); expect(updated?.status).toBe('completed'); }); });