task-queue.test.js
3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* Unit Tests — task-queue.js
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
const { TaskQueue, FAST_TIMEOUT, MAX_CONCURRENT } = require('../../src/engine/../../../server/services/task-queue');
describe('TaskQueue', () => {
let queue;
let mockChatService;
beforeEach(() => {
// Mock chat service with controllable sessions
mockChatService = {
sessions: new Map(),
getSession(id) { return this.sessions.get(id) || null; },
createSession(id) {
const session = {
id, status: 'disconnected',
connect: async () => { session.status = 'connected'; },
sendMessage: async (msg) => ({ result: `Echo: ${msg.substring(0, 50)}` }),
};
this.sessions.set(id, session);
return session;
},
};
queue = new TaskQueue(mockChatService);
});
afterEach(() => queue.stop());
describe('start / stop', () => {
it('should start heartbeat without error', () => {
expect(() => queue.start()).not.toThrow();
});
it('should stop heartbeat without error', () => {
queue.start();
expect(() => queue.stop()).not.toThrow();
});
it('should not double-start', () => {
queue.start();
queue.start(); // should be a no-op
queue.stop();
});
});
describe('getTask / listTasks', () => {
it('should return null for non-existent task', () => {
expect(queue.getTask('no-such-task')).toBeNull();
});
it('should return empty list initially', () => {
expect(queue.listTasks()).toEqual([]);
});
});
describe('dispatch', () => {
it('should create a task', async () => {
const result = await queue.dispatch('Test message', {});
expect(result.mode).toBe('async');
expect(result.taskId).toBeDefined();
expect(result.sessionId).toBeDefined();
});
it('should reject when max concurrent reached', async () => {
// Fill up to MAX_CONCURRENT
for (let i = 0; i < MAX_CONCURRENT; i++) {
// Create tasks that stay running (use slow sessions)
mockChatService.createSession = (id) => {
const s = {
id, status: 'disconnected',
connect: async () => { s.status = 'connected'; },
sendMessage: () => new Promise(() => {}), // never resolves
};
mockChatService.sessions.set(id, s);
return s;
};
await queue.dispatch(`Task ${i}`);
}
// Wait for tasks to start
await new Promise(r => setTimeout(r, 50));
// Next dispatch should be rejected
const result = await queue.dispatch('Overflow task');
expect(result.mode).toBe('rejected');
});
it('should track dispatched task', async () => {
const result = await queue.dispatch('Hello');
const task = queue.getTask(result.taskId);
expect(task).not.toBeNull();
expect(task.status).toBeDefined();
});
});
describe('listTasks', () => {
it('should filter by status', async () => {
await queue.dispatch('Test 1');
await new Promise(r => setTimeout(r, 50));
const completed = queue.listTasks({ status: 'completed' });
expect(Array.isArray(completed)).toBe(true);
});
it('should respect limit', async () => {
await queue.dispatch('A');
await queue.dispatch('B');
const tasks = queue.listTasks({ limit: 1 });
expect(tasks).toHaveLength(1);
});
});
describe('constants', () => {
it('should export FAST_TIMEOUT as 15s', () => {
expect(FAST_TIMEOUT).toBe(15000);
});
it('should export MAX_CONCURRENT as 3', () => {
expect(MAX_CONCURRENT).toBe(3);
});
});
});