@probelabs/probe 0.6.0-rc100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +583 -0
- package/bin/.gitkeep +0 -0
- package/bin/probe +158 -0
- package/bin/probe-binary +0 -0
- package/build/agent/ProbeAgent.d.ts +199 -0
- package/build/agent/ProbeAgent.js +1486 -0
- package/build/agent/acp/README.md +347 -0
- package/build/agent/acp/connection.js +237 -0
- package/build/agent/acp/connection.test.js +311 -0
- package/build/agent/acp/examples/simple-client.js +212 -0
- package/build/agent/acp/examples/tool-lifecycle.js +230 -0
- package/build/agent/acp/final-test.js +173 -0
- package/build/agent/acp/index.js +5 -0
- package/build/agent/acp/integration.test.js +385 -0
- package/build/agent/acp/manual-test.js +410 -0
- package/build/agent/acp/protocol-test.js +190 -0
- package/build/agent/acp/server.js +448 -0
- package/build/agent/acp/server.test.js +371 -0
- package/build/agent/acp/test-runner.js +216 -0
- package/build/agent/acp/test-utils/README.md +315 -0
- package/build/agent/acp/test-utils/acp-tester.js +484 -0
- package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
- package/build/agent/acp/tools.js +368 -0
- package/build/agent/acp/tools.test.js +334 -0
- package/build/agent/acp/types.js +218 -0
- package/build/agent/acp/types.test.js +327 -0
- package/build/agent/appTracer.js +360 -0
- package/build/agent/fileSpanExporter.js +169 -0
- package/build/agent/index.js +7426 -0
- package/build/agent/mcp/client.js +338 -0
- package/build/agent/mcp/config.js +313 -0
- package/build/agent/mcp/index.js +64 -0
- package/build/agent/mcp/xmlBridge.js +371 -0
- package/build/agent/mockProvider.js +53 -0
- package/build/agent/probeTool.js +257 -0
- package/build/agent/schemaUtils.js +1726 -0
- package/build/agent/simpleTelemetry.js +267 -0
- package/build/agent/telemetry.js +225 -0
- package/build/agent/tokenCounter.js +395 -0
- package/build/agent/tools.js +163 -0
- package/build/cli.js +49 -0
- package/build/delegate.js +267 -0
- package/build/directory-resolver.js +237 -0
- package/build/downloader.js +750 -0
- package/build/extract.js +149 -0
- package/build/index.js +70 -0
- package/build/mcp/index.js +514 -0
- package/build/mcp/index.ts +608 -0
- package/build/query.js +116 -0
- package/build/search.js +247 -0
- package/build/tools/common.js +410 -0
- package/build/tools/index.js +40 -0
- package/build/tools/langchain.js +88 -0
- package/build/tools/system-message.js +121 -0
- package/build/tools/vercel.js +271 -0
- package/build/utils/file-lister.js +193 -0
- package/build/utils.js +128 -0
- package/cjs/agent/ProbeAgent.cjs +5829 -0
- package/cjs/index.cjs +6217 -0
- package/cjs/package.json +3 -0
- package/index.d.ts +401 -0
- package/package.json +114 -0
- package/scripts/postinstall.js +172 -0
- package/src/agent/ProbeAgent.d.ts +199 -0
- package/src/agent/ProbeAgent.js +1486 -0
- package/src/agent/acp/README.md +347 -0
- package/src/agent/acp/connection.js +237 -0
- package/src/agent/acp/connection.test.js +311 -0
- package/src/agent/acp/examples/simple-client.js +212 -0
- package/src/agent/acp/examples/tool-lifecycle.js +230 -0
- package/src/agent/acp/final-test.js +173 -0
- package/src/agent/acp/index.js +5 -0
- package/src/agent/acp/integration.test.js +385 -0
- package/src/agent/acp/manual-test.js +410 -0
- package/src/agent/acp/protocol-test.js +190 -0
- package/src/agent/acp/server.js +448 -0
- package/src/agent/acp/server.test.js +371 -0
- package/src/agent/acp/test-runner.js +216 -0
- package/src/agent/acp/test-utils/README.md +315 -0
- package/src/agent/acp/test-utils/acp-tester.js +484 -0
- package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
- package/src/agent/acp/tools.js +368 -0
- package/src/agent/acp/tools.test.js +334 -0
- package/src/agent/acp/types.js +218 -0
- package/src/agent/acp/types.test.js +327 -0
- package/src/agent/appTracer.js +360 -0
- package/src/agent/fileSpanExporter.js +169 -0
- package/src/agent/index.js +813 -0
- package/src/agent/mcp/client.js +338 -0
- package/src/agent/mcp/config.js +313 -0
- package/src/agent/mcp/index.js +64 -0
- package/src/agent/mcp/xmlBridge.js +371 -0
- package/src/agent/mockProvider.js +53 -0
- package/src/agent/probeTool.js +257 -0
- package/src/agent/schemaUtils.js +1726 -0
- package/src/agent/simpleTelemetry.js +267 -0
- package/src/agent/telemetry.js +225 -0
- package/src/agent/tokenCounter.js +395 -0
- package/src/agent/tools.js +163 -0
- package/src/cli.js +49 -0
- package/src/delegate.js +267 -0
- package/src/directory-resolver.js +237 -0
- package/src/downloader.js +750 -0
- package/src/extract.js +149 -0
- package/src/index.js +70 -0
- package/src/mcp/index.ts +608 -0
- package/src/query.js +116 -0
- package/src/search.js +247 -0
- package/src/tools/common.js +410 -0
- package/src/tools/index.js +40 -0
- package/src/tools/langchain.js +88 -0
- package/src/tools/system-message.js +121 -0
- package/src/tools/vercel.js +271 -0
- package/src/utils/file-lister.js +193 -0
- package/src/utils.js +128 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// Integration tests for ACP implementation
|
|
2
|
+
import { jest } from '@jest/globals';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { ACPServer } from './server.js';
|
|
5
|
+
import { ACPConnection } from './connection.js';
|
|
6
|
+
import { ACP_PROTOCOL_VERSION, RequestMethod } from './types.js';
|
|
7
|
+
|
|
8
|
+
// Mock manually handled below
|
|
9
|
+
|
|
10
|
+
// Mock streams for testing
|
|
11
|
+
class MockStream extends EventEmitter {
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.encoding = null;
|
|
15
|
+
this.writtenData = [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setEncoding(encoding) {
|
|
19
|
+
this.encoding = encoding;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
write(data) {
|
|
23
|
+
this.writtenData.push(data);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('ACP Integration', () => {
|
|
29
|
+
let server, clientInput, clientOutput, serverInput, serverOutput;
|
|
30
|
+
let mockProbeAgent;
|
|
31
|
+
|
|
32
|
+
beforeEach(async () => {
|
|
33
|
+
// Setup mock streams - simulate client/server communication
|
|
34
|
+
clientInput = new MockStream(); // Client receives from server
|
|
35
|
+
clientOutput = new MockStream(); // Client sends to server
|
|
36
|
+
serverInput = new MockStream(); // Server receives from client
|
|
37
|
+
serverOutput = new MockStream(); // Server sends to client
|
|
38
|
+
|
|
39
|
+
// Connect the streams
|
|
40
|
+
clientOutput.pipe = jest.fn();
|
|
41
|
+
serverOutput.pipe = jest.fn();
|
|
42
|
+
|
|
43
|
+
// Mock ProbeAgent
|
|
44
|
+
const { ProbeAgent } = await import('../ProbeAgent.js');
|
|
45
|
+
mockProbeAgent = {
|
|
46
|
+
answer: jest.fn().mockResolvedValue('Test AI response'),
|
|
47
|
+
cancel: jest.fn(),
|
|
48
|
+
sessionId: 'test-session'
|
|
49
|
+
};
|
|
50
|
+
ProbeAgent.mockImplementation(() => mockProbeAgent);
|
|
51
|
+
|
|
52
|
+
// Create server with mock streams
|
|
53
|
+
server = new ACPServer({ debug: false });
|
|
54
|
+
server.connection = new ACPConnection(serverInput, serverOutput);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
if (server.connection) {
|
|
59
|
+
server.connection.close();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('full protocol flow', () => {
|
|
64
|
+
test('should handle complete initialization flow', async () => {
|
|
65
|
+
// Start server
|
|
66
|
+
server.connection.start();
|
|
67
|
+
|
|
68
|
+
// Simulate client initialization request
|
|
69
|
+
const initRequest = {
|
|
70
|
+
jsonrpc: '2.0',
|
|
71
|
+
method: RequestMethod.INITIALIZE,
|
|
72
|
+
params: { protocolVersion: ACP_PROTOCOL_VERSION },
|
|
73
|
+
id: 1
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Simulate receiving the request
|
|
77
|
+
serverInput.emit('data', JSON.stringify(initRequest) + '\n');
|
|
78
|
+
|
|
79
|
+
// Wait for async processing
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
81
|
+
|
|
82
|
+
// Check that server sent response
|
|
83
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
84
|
+
const response = JSON.parse(serverOutput.writtenData[0]);
|
|
85
|
+
|
|
86
|
+
expect(response).toEqual({
|
|
87
|
+
jsonrpc: '2.0',
|
|
88
|
+
id: 1,
|
|
89
|
+
result: {
|
|
90
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
91
|
+
serverInfo: {
|
|
92
|
+
name: 'probe-agent-acp',
|
|
93
|
+
version: '1.0.0',
|
|
94
|
+
description: 'Probe AI agent with code search capabilities'
|
|
95
|
+
},
|
|
96
|
+
capabilities: server.capabilities
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(server.initialized).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('should handle session creation and prompt flow', async () => {
|
|
104
|
+
server.connection.start();
|
|
105
|
+
server.initialized = true; // Skip initialization for this test
|
|
106
|
+
|
|
107
|
+
// 1. Create new session
|
|
108
|
+
const newSessionRequest = {
|
|
109
|
+
jsonrpc: '2.0',
|
|
110
|
+
method: RequestMethod.NEW_SESSION,
|
|
111
|
+
params: {},
|
|
112
|
+
id: 2
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
serverInput.emit('data', JSON.stringify(newSessionRequest) + '\n');
|
|
116
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
117
|
+
|
|
118
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
119
|
+
const sessionResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
120
|
+
expect(sessionResponse.result.sessionId).toBeDefined();
|
|
121
|
+
const sessionId = sessionResponse.result.sessionId;
|
|
122
|
+
|
|
123
|
+
// Clear previous data
|
|
124
|
+
serverOutput.writtenData = [];
|
|
125
|
+
|
|
126
|
+
// 2. Send prompt request
|
|
127
|
+
const promptRequest = {
|
|
128
|
+
jsonrpc: '2.0',
|
|
129
|
+
method: RequestMethod.PROMPT,
|
|
130
|
+
params: {
|
|
131
|
+
sessionId,
|
|
132
|
+
message: 'How does authentication work in this codebase?'
|
|
133
|
+
},
|
|
134
|
+
id: 3
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
serverInput.emit('data', JSON.stringify(promptRequest) + '\n');
|
|
138
|
+
await new Promise(resolve => setTimeout(resolve, 50)); // Give more time for AI processing
|
|
139
|
+
|
|
140
|
+
// Check that AI was called
|
|
141
|
+
expect(mockProbeAgent.answer).toHaveBeenCalledWith('How does authentication work in this codebase?');
|
|
142
|
+
|
|
143
|
+
// Check response
|
|
144
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
145
|
+
const promptResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
146
|
+
|
|
147
|
+
expect(promptResponse).toEqual({
|
|
148
|
+
jsonrpc: '2.0',
|
|
149
|
+
id: 3,
|
|
150
|
+
result: {
|
|
151
|
+
content: [{ type: 'text', text: 'Test AI response' }],
|
|
152
|
+
sessionId,
|
|
153
|
+
timestamp: expect.any(String)
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Check that session history was updated
|
|
158
|
+
const session = server.sessions.get(sessionId);
|
|
159
|
+
expect(session.history).toHaveLength(2);
|
|
160
|
+
expect(session.history[0].role).toBe('user');
|
|
161
|
+
expect(session.history[1].role).toBe('assistant');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('should handle errors gracefully', async () => {
|
|
165
|
+
server.connection.start();
|
|
166
|
+
|
|
167
|
+
// Send invalid method
|
|
168
|
+
const invalidRequest = {
|
|
169
|
+
jsonrpc: '2.0',
|
|
170
|
+
method: 'invalidMethod',
|
|
171
|
+
params: {},
|
|
172
|
+
id: 4
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
serverInput.emit('data', JSON.stringify(invalidRequest) + '\n');
|
|
176
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
177
|
+
|
|
178
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
179
|
+
const errorResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
180
|
+
|
|
181
|
+
expect(errorResponse).toEqual({
|
|
182
|
+
jsonrpc: '2.0',
|
|
183
|
+
id: 4,
|
|
184
|
+
error: {
|
|
185
|
+
code: -32601,
|
|
186
|
+
message: 'Unknown method: invalidMethod'
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('should handle session mode changes with notifications', async () => {
|
|
192
|
+
server.connection.start();
|
|
193
|
+
server.initialized = true;
|
|
194
|
+
|
|
195
|
+
// Create session
|
|
196
|
+
const newSessionRequest = {
|
|
197
|
+
jsonrpc: '2.0',
|
|
198
|
+
method: RequestMethod.NEW_SESSION,
|
|
199
|
+
params: {},
|
|
200
|
+
id: 5
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
serverInput.emit('data', JSON.stringify(newSessionRequest) + '\n');
|
|
204
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
205
|
+
|
|
206
|
+
const sessionResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
207
|
+
const sessionId = sessionResponse.result.sessionId;
|
|
208
|
+
|
|
209
|
+
// Clear previous data
|
|
210
|
+
serverOutput.writtenData = [];
|
|
211
|
+
|
|
212
|
+
// Set session mode
|
|
213
|
+
const setModeRequest = {
|
|
214
|
+
jsonrpc: '2.0',
|
|
215
|
+
method: RequestMethod.SET_SESSION_MODE,
|
|
216
|
+
params: {
|
|
217
|
+
sessionId,
|
|
218
|
+
mode: 'planning'
|
|
219
|
+
},
|
|
220
|
+
id: 6
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
serverInput.emit('data', JSON.stringify(setModeRequest) + '\n');
|
|
224
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
225
|
+
|
|
226
|
+
// Should have both response and notification
|
|
227
|
+
expect(serverOutput.writtenData).toHaveLength(2);
|
|
228
|
+
|
|
229
|
+
// Check response
|
|
230
|
+
const modeResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
231
|
+
expect(modeResponse).toEqual({
|
|
232
|
+
jsonrpc: '2.0',
|
|
233
|
+
id: 6,
|
|
234
|
+
result: { success: true }
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Check notification
|
|
238
|
+
const notification = JSON.parse(serverOutput.writtenData[1]);
|
|
239
|
+
expect(notification).toEqual({
|
|
240
|
+
jsonrpc: '2.0',
|
|
241
|
+
method: 'sessionUpdated',
|
|
242
|
+
params: {
|
|
243
|
+
sessionId,
|
|
244
|
+
mode: 'planning'
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('should handle cancellation', async () => {
|
|
250
|
+
server.connection.start();
|
|
251
|
+
server.initialized = true;
|
|
252
|
+
|
|
253
|
+
// Create session and trigger agent creation
|
|
254
|
+
const newSessionRequest = {
|
|
255
|
+
jsonrpc: '2.0',
|
|
256
|
+
method: RequestMethod.NEW_SESSION,
|
|
257
|
+
params: {},
|
|
258
|
+
id: 7
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
serverInput.emit('data', JSON.stringify(newSessionRequest) + '\n');
|
|
262
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
263
|
+
|
|
264
|
+
const sessionResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
265
|
+
const sessionId = sessionResponse.result.sessionId;
|
|
266
|
+
|
|
267
|
+
// Send a prompt to create the agent
|
|
268
|
+
const promptRequest = {
|
|
269
|
+
jsonrpc: '2.0',
|
|
270
|
+
method: RequestMethod.PROMPT,
|
|
271
|
+
params: {
|
|
272
|
+
sessionId,
|
|
273
|
+
message: 'Test question'
|
|
274
|
+
},
|
|
275
|
+
id: 8
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
serverInput.emit('data', JSON.stringify(promptRequest) + '\n');
|
|
279
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
280
|
+
|
|
281
|
+
// Clear previous data
|
|
282
|
+
serverOutput.writtenData = [];
|
|
283
|
+
|
|
284
|
+
// Send cancel request
|
|
285
|
+
const cancelRequest = {
|
|
286
|
+
jsonrpc: '2.0',
|
|
287
|
+
method: RequestMethod.CANCEL,
|
|
288
|
+
params: { sessionId },
|
|
289
|
+
id: 9
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
serverInput.emit('data', JSON.stringify(cancelRequest) + '\n');
|
|
293
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
294
|
+
|
|
295
|
+
// Check that cancel was called on agent
|
|
296
|
+
expect(mockProbeAgent.cancel).toHaveBeenCalled();
|
|
297
|
+
|
|
298
|
+
// Check response
|
|
299
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
300
|
+
const cancelResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
301
|
+
expect(cancelResponse).toEqual({
|
|
302
|
+
jsonrpc: '2.0',
|
|
303
|
+
id: 9,
|
|
304
|
+
result: { success: true }
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe('error handling', () => {
|
|
310
|
+
beforeEach(() => {
|
|
311
|
+
server.connection.start();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('should handle malformed JSON', async () => {
|
|
315
|
+
serverInput.emit('data', 'malformed json\n');
|
|
316
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
317
|
+
|
|
318
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
319
|
+
const errorResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
320
|
+
expect(errorResponse.error.code).toBe(-32700); // Parse error
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('should handle invalid JSON-RPC format', async () => {
|
|
324
|
+
const invalidMessage = { jsonrpc: '1.0', method: 'test' };
|
|
325
|
+
serverInput.emit('data', JSON.stringify(invalidMessage) + '\n');
|
|
326
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
327
|
+
|
|
328
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
329
|
+
const errorResponse = JSON.parse(serverOutput.writtenData[0]);
|
|
330
|
+
expect(errorResponse.error.code).toBe(-32600); // Invalid request
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test('should handle AI errors in prompt processing', async () => {
|
|
334
|
+
server.initialized = true;
|
|
335
|
+
|
|
336
|
+
// Mock AI to throw error
|
|
337
|
+
mockProbeAgent.answer.mockRejectedValue(new Error('AI service unavailable'));
|
|
338
|
+
|
|
339
|
+
// Create session
|
|
340
|
+
await server.handleNewSession({ sessionId: 'test-session' });
|
|
341
|
+
|
|
342
|
+
const promptRequest = {
|
|
343
|
+
jsonrpc: '2.0',
|
|
344
|
+
method: RequestMethod.PROMPT,
|
|
345
|
+
params: {
|
|
346
|
+
sessionId: 'test-session',
|
|
347
|
+
message: 'Test question'
|
|
348
|
+
},
|
|
349
|
+
id: 10
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
serverInput.emit('data', JSON.stringify(promptRequest) + '\n');
|
|
353
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
354
|
+
|
|
355
|
+
expect(serverOutput.writtenData).toHaveLength(1);
|
|
356
|
+
const response = JSON.parse(serverOutput.writtenData[0]);
|
|
357
|
+
|
|
358
|
+
expect(response.result.content[0].text).toBe('Error: AI service unavailable');
|
|
359
|
+
expect(response.result.error).toBe(true);
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
describe('connection management', () => {
|
|
364
|
+
test('should clean up on disconnect', async () => {
|
|
365
|
+
server.connection.start();
|
|
366
|
+
server.initialized = true;
|
|
367
|
+
|
|
368
|
+
// Create some sessions
|
|
369
|
+
await server.handleNewSession({ sessionId: 'session1' });
|
|
370
|
+
await server.handleNewSession({ sessionId: 'session2' });
|
|
371
|
+
await server.handlePrompt({ sessionId: 'session1', message: 'test' });
|
|
372
|
+
await server.handlePrompt({ sessionId: 'session2', message: 'test' });
|
|
373
|
+
|
|
374
|
+
expect(server.sessions.size).toBe(2);
|
|
375
|
+
|
|
376
|
+
// Simulate disconnect
|
|
377
|
+
serverInput.emit('end');
|
|
378
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
379
|
+
|
|
380
|
+
// All sessions should be cleaned up
|
|
381
|
+
expect(server.sessions.size).toBe(0);
|
|
382
|
+
expect(mockProbeAgent.cancel).toHaveBeenCalledTimes(2);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
});
|