Download src/server/__tests__/tasks.test.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 5.32 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/__tests__/tasks.test.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/__tests__/tasks.test.ts
-
curl -L -o tasks.test.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/server/__tests__/tasks.test.ts
5.32 kB
| /** | |
| * Unit tests for TaskService and Tasks API | |
| */ | |
| import { describe, it, expect, beforeEach, afterEach } from 'bun:test' | |
| import * as fs from 'fs/promises' | |
| import * as path from 'path' | |
| import * as os from 'os' | |
| import { TaskService } from '../services/taskService.js' | |
| // ============================================================================ | |
| // TaskService unit tests | |
| // ============================================================================ | |
| describe('TaskService', () => { | |
| let tmpDir: string | |
| beforeEach(async () => { | |
| tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'claude-tasks-')) | |
| process.env.CLAUDE_CONFIG_DIR = tmpDir | |
| }) | |
| afterEach(async () => { | |
| await fs.rm(tmpDir, { recursive: true, force: true }) | |
| delete process.env.CLAUDE_CONFIG_DIR | |
| }) | |
| it('should return empty list when no tasks dir', async () => { | |
| const svc = new TaskService() | |
| const tasks = await svc.listTasks() | |
| expect(tasks).toEqual([]) | |
| }) | |
| it('should list tasks from JSON files', async () => { | |
| const tasksDir = path.join(tmpDir, 'tasks') | |
| await fs.mkdir(tasksDir, { recursive: true }) | |
| await fs.writeFile(path.join(tasksDir, 'task-001.json'), JSON.stringify({ | |
| id: 'task-001', | |
| type: 'local_agent', | |
| status: 'completed', | |
| name: 'code-review', | |
| description: 'Review PR #42', | |
| createdAt: Date.now() - 60000, | |
| completedAt: Date.now(), | |
| })) | |
| await fs.writeFile(path.join(tasksDir, 'task-002.json'), JSON.stringify({ | |
| id: 'task-002', | |
| type: 'in_process_teammate', | |
| status: 'running', | |
| name: 'frontend-dev', | |
| teamName: 'ui-team', | |
| createdAt: Date.now(), | |
| })) | |
| const svc = new TaskService() | |
| const tasks = await svc.listTasks() | |
| expect(tasks.length).toBe(2) | |
| // 按 createdAt 倒序 | |
| expect(tasks[0].id).toBe('task-002') | |
| expect(tasks[1].id).toBe('task-001') | |
| }) | |
| it('should scan nested team task directories', async () => { | |
| const teamDir = path.join(tmpDir, 'tasks', 'my-team') | |
| await fs.mkdir(teamDir, { recursive: true }) | |
| await fs.writeFile(path.join(teamDir, 'member-1.json'), JSON.stringify({ | |
| id: 'member-1', | |
| type: 'in_process_teammate', | |
| status: 'completed', | |
| teamName: 'my-team', | |
| })) | |
| const svc = new TaskService() | |
| const tasks = await svc.listTasks() | |
| expect(tasks.length).toBe(1) | |
| expect(tasks[0].teamName).toBe('my-team') | |
| }) | |
| it('should get single task by ID', async () => { | |
| const tasksDir = path.join(tmpDir, 'tasks') | |
| await fs.mkdir(tasksDir, { recursive: true }) | |
| await fs.writeFile(path.join(tasksDir, 'abc.json'), JSON.stringify({ | |
| id: 'abc', | |
| type: 'local_shell', | |
| status: 'failed', | |
| name: 'build', | |
| })) | |
| const svc = new TaskService() | |
| const task = await svc.getTask('abc') | |
| expect(task).toBeDefined() | |
| expect(task!.status).toBe('failed') | |
| }) | |
| it('should return null for unknown task', async () => { | |
| const svc = new TaskService() | |
| const task = await svc.getTask('nonexistent') | |
| expect(task).toBeNull() | |
| }) | |
| it('should skip invalid JSON files gracefully', async () => { | |
| const tasksDir = path.join(tmpDir, 'tasks') | |
| await fs.mkdir(tasksDir, { recursive: true }) | |
| await fs.writeFile(path.join(tasksDir, 'bad.json'), 'not json {{{') | |
| const svc = new TaskService() | |
| const tasks = await svc.listTasks() | |
| expect(tasks).toEqual([]) | |
| }) | |
| }) | |
| // ============================================================================ | |
| // Tasks API integration tests | |
| // ============================================================================ | |
| describe('Tasks API', () => { | |
| let server: any | |
| let baseUrl: string | |
| let tmpDir: string | |
| beforeEach(async () => { | |
| tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'claude-tasks-api-')) | |
| process.env.CLAUDE_CONFIG_DIR = tmpDir | |
| await fs.mkdir(path.join(tmpDir, 'projects'), { recursive: true }) | |
| const port = 15500 + Math.floor(Math.random() * 500) | |
| const { startServer } = await import('../../server/index.js') | |
| server = startServer(port, '127.0.0.1') | |
| baseUrl = `http://127.0.0.1:${port}` | |
| }) | |
| afterEach(async () => { | |
| server?.stop() | |
| await fs.rm(tmpDir, { recursive: true, force: true }) | |
| delete process.env.CLAUDE_CONFIG_DIR | |
| }) | |
| it('should return empty tasks list', async () => { | |
| const res = await fetch(`${baseUrl}/api/tasks`) | |
| const data = await res.json() | |
| expect(res.status).toBe(200) | |
| expect(data.tasks).toEqual([]) | |
| }) | |
| it('should return tasks when files exist', async () => { | |
| const tasksDir = path.join(tmpDir, 'tasks') | |
| await fs.mkdir(tasksDir, { recursive: true }) | |
| await fs.writeFile(path.join(tasksDir, 'test.json'), JSON.stringify({ | |
| id: 'test', type: 'local_agent', status: 'completed', name: 'test-task', | |
| })) | |
| const res = await fetch(`${baseUrl}/api/tasks`) | |
| const data = await res.json() | |
| expect(data.tasks.length).toBe(1) | |
| expect(data.tasks[0].name).toBe('test-task') | |
| }) | |
| it('should return 404 for unknown task', async () => { | |
| const res = await fetch(`${baseUrl}/api/tasks/nonexistent`) | |
| expect(res.status).toBe(404) | |
| }) | |
| it('should reject non-GET methods', async () => { | |
| const res = await fetch(`${baseUrl}/api/tasks`, { method: 'POST' }) | |
| expect(res.status).toBe(405) | |
| }) | |
| }) | |