@cloudflare/sandbox 0.4.12 → 0.4.14
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/.turbo/turbo-build.log +13 -47
- package/CHANGELOG.md +38 -16
- package/Dockerfile +15 -9
- package/README.md +0 -1
- package/dist/index.d.ts +1889 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3144 -65
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/clients/base-client.ts +39 -24
- package/src/clients/command-client.ts +8 -8
- package/src/clients/file-client.ts +31 -26
- package/src/clients/git-client.ts +3 -4
- package/src/clients/index.ts +12 -16
- package/src/clients/interpreter-client.ts +51 -47
- package/src/clients/port-client.ts +10 -10
- package/src/clients/process-client.ts +11 -8
- package/src/clients/sandbox-client.ts +2 -4
- package/src/clients/types.ts +6 -2
- package/src/clients/utility-client.ts +10 -6
- package/src/errors/adapter.ts +90 -32
- package/src/errors/classes.ts +189 -64
- package/src/errors/index.ts +9 -5
- package/src/file-stream.ts +11 -6
- package/src/index.ts +22 -15
- package/src/interpreter.ts +50 -41
- package/src/request-handler.ts +24 -21
- package/src/sandbox.ts +339 -149
- package/src/security.ts +21 -6
- package/src/sse-parser.ts +4 -3
- package/src/version.ts +1 -1
- package/tests/base-client.test.ts +116 -80
- package/tests/command-client.test.ts +149 -112
- package/tests/file-client.test.ts +309 -197
- package/tests/file-stream.test.ts +24 -20
- package/tests/get-sandbox.test.ts +10 -10
- package/tests/git-client.test.ts +188 -101
- package/tests/port-client.test.ts +100 -108
- package/tests/process-client.test.ts +204 -179
- package/tests/request-handler.test.ts +117 -65
- package/tests/sandbox.test.ts +219 -67
- package/tests/sse-parser.test.ts +17 -16
- package/tests/utility-client.test.ts +79 -72
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +6 -6
- package/dist/chunk-BFVUNTP4.js +0 -104
- package/dist/chunk-BFVUNTP4.js.map +0 -1
- package/dist/chunk-EKSWCBCA.js +0 -86
- package/dist/chunk-EKSWCBCA.js.map +0 -1
- package/dist/chunk-JXZMAU2C.js +0 -559
- package/dist/chunk-JXZMAU2C.js.map +0 -1
- package/dist/chunk-UJ3TV4M6.js +0 -7
- package/dist/chunk-UJ3TV4M6.js.map +0 -1
- package/dist/chunk-YE265ASX.js +0 -2484
- package/dist/chunk-YE265ASX.js.map +0 -1
- package/dist/chunk-Z532A7QC.js +0 -78
- package/dist/chunk-Z532A7QC.js.map +0 -1
- package/dist/file-stream.d.ts +0 -43
- package/dist/file-stream.js +0 -9
- package/dist/file-stream.js.map +0 -1
- package/dist/interpreter.d.ts +0 -33
- package/dist/interpreter.js +0 -8
- package/dist/interpreter.js.map +0 -1
- package/dist/request-handler.d.ts +0 -18
- package/dist/request-handler.js +0 -13
- package/dist/request-handler.js.map +0 -1
- package/dist/sandbox-CLZWpfGc.d.ts +0 -613
- package/dist/sandbox.d.ts +0 -4
- package/dist/sandbox.js +0 -13
- package/dist/sandbox.js.map +0 -1
- package/dist/security.d.ts +0 -31
- package/dist/security.js +0 -13
- package/dist/security.js.map +0 -1
- package/dist/sse-parser.d.ts +0 -28
- package/dist/sse-parser.js +0 -11
- package/dist/sse-parser.js.map +0 -1
- package/dist/version.d.ts +0 -8
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
|
@@ -21,13 +21,13 @@ describe('ProcessClient', () => {
|
|
|
21
21
|
|
|
22
22
|
beforeEach(() => {
|
|
23
23
|
vi.clearAllMocks();
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
mockFetch = vi.fn();
|
|
26
26
|
global.fetch = mockFetch as unknown as typeof fetch;
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
client = new ProcessClient({
|
|
29
29
|
baseUrl: 'http://test.com',
|
|
30
|
-
port: 3000
|
|
30
|
+
port: 3000
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -44,15 +44,14 @@ describe('ProcessClient', () => {
|
|
|
44
44
|
command: 'npm run dev',
|
|
45
45
|
status: 'running',
|
|
46
46
|
pid: 12345,
|
|
47
|
-
startTime: '2023-01-01T00:00:00Z'
|
|
47
|
+
startTime: '2023-01-01T00:00:00Z'
|
|
48
48
|
},
|
|
49
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
49
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
mockFetch.mockResolvedValue(
|
|
53
|
-
JSON.stringify(mockResponse),
|
|
54
|
-
|
|
55
|
-
));
|
|
52
|
+
mockFetch.mockResolvedValue(
|
|
53
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
54
|
+
);
|
|
56
55
|
|
|
57
56
|
const result = await client.startProcess('npm run dev', 'session-123');
|
|
58
57
|
|
|
@@ -71,17 +70,18 @@ describe('ProcessClient', () => {
|
|
|
71
70
|
command: 'python app.py',
|
|
72
71
|
status: 'running',
|
|
73
72
|
pid: 54321,
|
|
74
|
-
startTime: '2023-01-01T00:00:00Z'
|
|
73
|
+
startTime: '2023-01-01T00:00:00Z'
|
|
75
74
|
},
|
|
76
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
75
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
77
76
|
};
|
|
78
77
|
|
|
79
|
-
mockFetch.mockResolvedValue(
|
|
80
|
-
JSON.stringify(mockResponse),
|
|
81
|
-
|
|
82
|
-
));
|
|
78
|
+
mockFetch.mockResolvedValue(
|
|
79
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
80
|
+
);
|
|
83
81
|
|
|
84
|
-
const result = await client.startProcess('python app.py', 'session-456', {
|
|
82
|
+
const result = await client.startProcess('python app.py', 'session-456', {
|
|
83
|
+
processId: 'my-api-server'
|
|
84
|
+
});
|
|
85
85
|
|
|
86
86
|
expect(result.success).toBe(true);
|
|
87
87
|
expect(result.process.id).toBe('my-api-server');
|
|
@@ -97,21 +97,28 @@ describe('ProcessClient', () => {
|
|
|
97
97
|
command: 'docker run postgres',
|
|
98
98
|
status: 'running',
|
|
99
99
|
pid: 99999,
|
|
100
|
-
startTime: '2023-01-01T00:00:00Z'
|
|
100
|
+
startTime: '2023-01-01T00:00:00Z'
|
|
101
101
|
},
|
|
102
|
-
timestamp: '2023-01-01T00:00:05Z'
|
|
102
|
+
timestamp: '2023-01-01T00:00:05Z'
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
-
mockFetch.mockImplementation(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
mockFetch.mockImplementation(
|
|
106
|
+
() =>
|
|
107
|
+
new Promise((resolve) =>
|
|
108
|
+
setTimeout(
|
|
109
|
+
() =>
|
|
110
|
+
resolve(
|
|
111
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
112
|
+
),
|
|
113
|
+
100
|
|
114
|
+
)
|
|
115
|
+
)
|
|
112
116
|
);
|
|
113
117
|
|
|
114
|
-
const result = await client.startProcess(
|
|
118
|
+
const result = await client.startProcess(
|
|
119
|
+
'docker run postgres',
|
|
120
|
+
'session-789'
|
|
121
|
+
);
|
|
115
122
|
|
|
116
123
|
expect(result.success).toBe(true);
|
|
117
124
|
expect(result.process.status).toBe('running');
|
|
@@ -124,13 +131,13 @@ describe('ProcessClient', () => {
|
|
|
124
131
|
code: 'COMMAND_NOT_FOUND'
|
|
125
132
|
};
|
|
126
133
|
|
|
127
|
-
mockFetch.mockResolvedValue(
|
|
128
|
-
JSON.stringify(errorResponse),
|
|
129
|
-
|
|
130
|
-
));
|
|
134
|
+
mockFetch.mockResolvedValue(
|
|
135
|
+
new Response(JSON.stringify(errorResponse), { status: 404 })
|
|
136
|
+
);
|
|
131
137
|
|
|
132
|
-
await expect(
|
|
133
|
-
.
|
|
138
|
+
await expect(
|
|
139
|
+
client.startProcess('invalidcmd', 'session-err')
|
|
140
|
+
).rejects.toThrow(CommandNotFoundError);
|
|
134
141
|
});
|
|
135
142
|
|
|
136
143
|
it('should handle process startup failures', async () => {
|
|
@@ -139,13 +146,13 @@ describe('ProcessClient', () => {
|
|
|
139
146
|
code: 'PROCESS_ERROR'
|
|
140
147
|
};
|
|
141
148
|
|
|
142
|
-
mockFetch.mockResolvedValue(
|
|
143
|
-
JSON.stringify(errorResponse),
|
|
144
|
-
|
|
145
|
-
));
|
|
149
|
+
mockFetch.mockResolvedValue(
|
|
150
|
+
new Response(JSON.stringify(errorResponse), { status: 500 })
|
|
151
|
+
);
|
|
146
152
|
|
|
147
|
-
await expect(
|
|
148
|
-
.
|
|
153
|
+
await expect(
|
|
154
|
+
client.startProcess('sudo privileged-command', 'session-err')
|
|
155
|
+
).rejects.toThrow(ProcessError);
|
|
149
156
|
});
|
|
150
157
|
});
|
|
151
158
|
|
|
@@ -159,14 +166,14 @@ describe('ProcessClient', () => {
|
|
|
159
166
|
command: 'npm run dev',
|
|
160
167
|
status: 'running',
|
|
161
168
|
pid: 12345,
|
|
162
|
-
startTime: '2023-01-01T00:00:00Z'
|
|
169
|
+
startTime: '2023-01-01T00:00:00Z'
|
|
163
170
|
},
|
|
164
171
|
{
|
|
165
172
|
id: 'proc-api',
|
|
166
173
|
command: 'python api.py',
|
|
167
174
|
status: 'running',
|
|
168
175
|
pid: 12346,
|
|
169
|
-
startTime: '2023-01-01T00:00:30Z'
|
|
176
|
+
startTime: '2023-01-01T00:00:30Z'
|
|
170
177
|
},
|
|
171
178
|
{
|
|
172
179
|
id: 'proc-worker',
|
|
@@ -175,17 +182,16 @@ describe('ProcessClient', () => {
|
|
|
175
182
|
pid: 12347,
|
|
176
183
|
exitCode: 0,
|
|
177
184
|
startTime: '2023-01-01T00:01:00Z',
|
|
178
|
-
endTime: '2023-01-01T00:05:00Z'
|
|
185
|
+
endTime: '2023-01-01T00:05:00Z'
|
|
179
186
|
}
|
|
180
187
|
],
|
|
181
188
|
count: 3,
|
|
182
|
-
timestamp: '2023-01-01T00:05:30Z'
|
|
189
|
+
timestamp: '2023-01-01T00:05:30Z'
|
|
183
190
|
};
|
|
184
191
|
|
|
185
|
-
mockFetch.mockResolvedValue(
|
|
186
|
-
JSON.stringify(mockResponse),
|
|
187
|
-
|
|
188
|
-
));
|
|
192
|
+
mockFetch.mockResolvedValue(
|
|
193
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
194
|
+
);
|
|
189
195
|
|
|
190
196
|
const result = await client.listProcesses('session-list');
|
|
191
197
|
|
|
@@ -193,12 +199,16 @@ describe('ProcessClient', () => {
|
|
|
193
199
|
expect(result.count).toBe(3);
|
|
194
200
|
expect(result.processes).toHaveLength(3);
|
|
195
201
|
|
|
196
|
-
const runningProcesses = result.processes.filter(
|
|
202
|
+
const runningProcesses = result.processes.filter(
|
|
203
|
+
(p) => p.status === 'running'
|
|
204
|
+
);
|
|
197
205
|
expect(runningProcesses).toHaveLength(2);
|
|
198
206
|
expect(runningProcesses[0].pid).toBeDefined();
|
|
199
207
|
expect(runningProcesses[1].pid).toBeDefined();
|
|
200
208
|
|
|
201
|
-
const completedProcess = result.processes.find(
|
|
209
|
+
const completedProcess = result.processes.find(
|
|
210
|
+
(p) => p.status === 'completed'
|
|
211
|
+
);
|
|
202
212
|
expect(completedProcess?.exitCode).toBe(0);
|
|
203
213
|
expect(completedProcess?.endTime).toBeDefined();
|
|
204
214
|
});
|
|
@@ -211,15 +221,14 @@ describe('ProcessClient', () => {
|
|
|
211
221
|
command: 'python analytics.py --batch-size=1000',
|
|
212
222
|
status: 'running',
|
|
213
223
|
pid: 98765,
|
|
214
|
-
startTime: '2023-01-01T00:00:00Z'
|
|
224
|
+
startTime: '2023-01-01T00:00:00Z'
|
|
215
225
|
},
|
|
216
|
-
timestamp: '2023-01-01T00:10:00Z'
|
|
226
|
+
timestamp: '2023-01-01T00:10:00Z'
|
|
217
227
|
};
|
|
218
228
|
|
|
219
|
-
mockFetch.mockResolvedValue(
|
|
220
|
-
JSON.stringify(mockResponse),
|
|
221
|
-
|
|
222
|
-
));
|
|
229
|
+
mockFetch.mockResolvedValue(
|
|
230
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
231
|
+
);
|
|
223
232
|
|
|
224
233
|
const result = await client.getProcess('proc-analytics', 'session-get');
|
|
225
234
|
|
|
@@ -236,13 +245,13 @@ describe('ProcessClient', () => {
|
|
|
236
245
|
code: 'PROCESS_NOT_FOUND'
|
|
237
246
|
};
|
|
238
247
|
|
|
239
|
-
mockFetch.mockResolvedValue(
|
|
240
|
-
JSON.stringify(errorResponse),
|
|
241
|
-
|
|
242
|
-
));
|
|
248
|
+
mockFetch.mockResolvedValue(
|
|
249
|
+
new Response(JSON.stringify(errorResponse), { status: 404 })
|
|
250
|
+
);
|
|
243
251
|
|
|
244
|
-
await expect(
|
|
245
|
-
.
|
|
252
|
+
await expect(
|
|
253
|
+
client.getProcess('nonexistent-proc', 'session-err')
|
|
254
|
+
).rejects.toThrow(ProcessNotFoundError);
|
|
246
255
|
});
|
|
247
256
|
|
|
248
257
|
it('should handle empty process list', async () => {
|
|
@@ -250,13 +259,12 @@ describe('ProcessClient', () => {
|
|
|
250
259
|
success: true,
|
|
251
260
|
processes: [],
|
|
252
261
|
count: 0,
|
|
253
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
262
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
254
263
|
};
|
|
255
264
|
|
|
256
|
-
mockFetch.mockResolvedValue(
|
|
257
|
-
JSON.stringify(mockResponse),
|
|
258
|
-
|
|
259
|
-
));
|
|
265
|
+
mockFetch.mockResolvedValue(
|
|
266
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
267
|
+
);
|
|
260
268
|
|
|
261
269
|
const result = await client.listProcesses('session-list');
|
|
262
270
|
|
|
@@ -271,13 +279,12 @@ describe('ProcessClient', () => {
|
|
|
271
279
|
const mockResponse: KillProcessResponse = {
|
|
272
280
|
success: true,
|
|
273
281
|
message: 'Process proc-web killed successfully',
|
|
274
|
-
timestamp: '2023-01-01T00:10:00Z'
|
|
282
|
+
timestamp: '2023-01-01T00:10:00Z'
|
|
275
283
|
};
|
|
276
284
|
|
|
277
|
-
mockFetch.mockResolvedValue(
|
|
278
|
-
JSON.stringify(mockResponse),
|
|
279
|
-
|
|
280
|
-
));
|
|
285
|
+
mockFetch.mockResolvedValue(
|
|
286
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
287
|
+
);
|
|
281
288
|
|
|
282
289
|
const result = await client.killProcess('proc-web', 'session-kill');
|
|
283
290
|
|
|
@@ -292,13 +299,13 @@ describe('ProcessClient', () => {
|
|
|
292
299
|
code: 'PROCESS_NOT_FOUND'
|
|
293
300
|
};
|
|
294
301
|
|
|
295
|
-
mockFetch.mockResolvedValue(
|
|
296
|
-
JSON.stringify(errorResponse),
|
|
297
|
-
|
|
298
|
-
));
|
|
302
|
+
mockFetch.mockResolvedValue(
|
|
303
|
+
new Response(JSON.stringify(errorResponse), { status: 404 })
|
|
304
|
+
);
|
|
299
305
|
|
|
300
|
-
await expect(
|
|
301
|
-
.
|
|
306
|
+
await expect(
|
|
307
|
+
client.killProcess('already-dead-proc', 'session-err')
|
|
308
|
+
).rejects.toThrow(ProcessNotFoundError);
|
|
302
309
|
});
|
|
303
310
|
|
|
304
311
|
it('should kill all processes at once', async () => {
|
|
@@ -306,13 +313,12 @@ describe('ProcessClient', () => {
|
|
|
306
313
|
success: true,
|
|
307
314
|
killedCount: 5,
|
|
308
315
|
message: 'All 5 processes killed successfully',
|
|
309
|
-
timestamp: '2023-01-01T00:15:00Z'
|
|
316
|
+
timestamp: '2023-01-01T00:15:00Z'
|
|
310
317
|
};
|
|
311
318
|
|
|
312
|
-
mockFetch.mockResolvedValue(
|
|
313
|
-
JSON.stringify(mockResponse),
|
|
314
|
-
|
|
315
|
-
));
|
|
319
|
+
mockFetch.mockResolvedValue(
|
|
320
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
321
|
+
);
|
|
316
322
|
|
|
317
323
|
const result = await client.killAllProcesses('session-killall');
|
|
318
324
|
|
|
@@ -326,13 +332,12 @@ describe('ProcessClient', () => {
|
|
|
326
332
|
success: true,
|
|
327
333
|
killedCount: 0,
|
|
328
334
|
message: 'No processes to kill',
|
|
329
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
335
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
330
336
|
};
|
|
331
337
|
|
|
332
|
-
mockFetch.mockResolvedValue(
|
|
333
|
-
JSON.stringify(mockResponse),
|
|
334
|
-
|
|
335
|
-
));
|
|
338
|
+
mockFetch.mockResolvedValue(
|
|
339
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
340
|
+
);
|
|
336
341
|
|
|
337
342
|
const result = await client.killAllProcesses('session-killall');
|
|
338
343
|
|
|
@@ -347,13 +352,13 @@ describe('ProcessClient', () => {
|
|
|
347
352
|
code: 'PROCESS_ERROR'
|
|
348
353
|
};
|
|
349
354
|
|
|
350
|
-
mockFetch.mockResolvedValue(
|
|
351
|
-
JSON.stringify(errorResponse),
|
|
352
|
-
|
|
353
|
-
));
|
|
355
|
+
mockFetch.mockResolvedValue(
|
|
356
|
+
new Response(JSON.stringify(errorResponse), { status: 500 })
|
|
357
|
+
);
|
|
354
358
|
|
|
355
|
-
await expect(
|
|
356
|
-
.
|
|
359
|
+
await expect(
|
|
360
|
+
client.killProcess('protected-proc', 'session-err')
|
|
361
|
+
).rejects.toThrow(ProcessError);
|
|
357
362
|
});
|
|
358
363
|
});
|
|
359
364
|
|
|
@@ -370,13 +375,12 @@ describe('ProcessClient', () => {
|
|
|
370
375
|
[INFO] Response: 200 OK`,
|
|
371
376
|
stderr: `[WARN] Deprecated function used in auth.js:45
|
|
372
377
|
[WARN] High memory usage: 85%`,
|
|
373
|
-
timestamp: '2023-01-01T00:10:00Z'
|
|
378
|
+
timestamp: '2023-01-01T00:10:00Z'
|
|
374
379
|
};
|
|
375
380
|
|
|
376
|
-
mockFetch.mockResolvedValue(
|
|
377
|
-
JSON.stringify(mockResponse),
|
|
378
|
-
|
|
379
|
-
));
|
|
381
|
+
mockFetch.mockResolvedValue(
|
|
382
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
383
|
+
);
|
|
380
384
|
|
|
381
385
|
const result = await client.getProcessLogs('proc-server', 'session-logs');
|
|
382
386
|
|
|
@@ -394,13 +398,13 @@ describe('ProcessClient', () => {
|
|
|
394
398
|
code: 'PROCESS_NOT_FOUND'
|
|
395
399
|
};
|
|
396
400
|
|
|
397
|
-
mockFetch.mockResolvedValue(
|
|
398
|
-
JSON.stringify(errorResponse),
|
|
399
|
-
|
|
400
|
-
));
|
|
401
|
+
mockFetch.mockResolvedValue(
|
|
402
|
+
new Response(JSON.stringify(errorResponse), { status: 404 })
|
|
403
|
+
);
|
|
401
404
|
|
|
402
|
-
await expect(
|
|
403
|
-
.
|
|
405
|
+
await expect(
|
|
406
|
+
client.getProcessLogs('missing-proc', 'session-err')
|
|
407
|
+
).rejects.toThrow(ProcessNotFoundError);
|
|
404
408
|
});
|
|
405
409
|
|
|
406
410
|
it('should retrieve logs for processes with large output', async () => {
|
|
@@ -412,13 +416,12 @@ describe('ProcessClient', () => {
|
|
|
412
416
|
processId: 'proc-batch',
|
|
413
417
|
stdout: largeStdout,
|
|
414
418
|
stderr: largeStderr,
|
|
415
|
-
timestamp: '2023-01-01T00:30:00Z'
|
|
419
|
+
timestamp: '2023-01-01T00:30:00Z'
|
|
416
420
|
};
|
|
417
421
|
|
|
418
|
-
mockFetch.mockResolvedValue(
|
|
419
|
-
JSON.stringify(mockResponse),
|
|
420
|
-
|
|
421
|
-
));
|
|
422
|
+
mockFetch.mockResolvedValue(
|
|
423
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
424
|
+
);
|
|
422
425
|
|
|
423
426
|
const result = await client.getProcessLogs('proc-batch', 'session-logs');
|
|
424
427
|
|
|
@@ -435,13 +438,12 @@ describe('ProcessClient', () => {
|
|
|
435
438
|
processId: 'proc-silent',
|
|
436
439
|
stdout: '',
|
|
437
440
|
stderr: '',
|
|
438
|
-
timestamp: '2023-01-01T00:05:00Z'
|
|
441
|
+
timestamp: '2023-01-01T00:05:00Z'
|
|
439
442
|
};
|
|
440
443
|
|
|
441
|
-
mockFetch.mockResolvedValue(
|
|
442
|
-
JSON.stringify(mockResponse),
|
|
443
|
-
|
|
444
|
-
));
|
|
444
|
+
mockFetch.mockResolvedValue(
|
|
445
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
446
|
+
);
|
|
445
447
|
|
|
446
448
|
const result = await client.getProcessLogs('proc-silent', 'session-logs');
|
|
447
449
|
|
|
@@ -471,12 +473,17 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
471
473
|
}
|
|
472
474
|
});
|
|
473
475
|
|
|
474
|
-
mockFetch.mockResolvedValue(
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
476
|
+
mockFetch.mockResolvedValue(
|
|
477
|
+
new Response(mockStream, {
|
|
478
|
+
status: 200,
|
|
479
|
+
headers: { 'Content-Type': 'text/event-stream' }
|
|
480
|
+
})
|
|
481
|
+
);
|
|
478
482
|
|
|
479
|
-
const stream = await client.streamProcessLogs(
|
|
483
|
+
const stream = await client.streamProcessLogs(
|
|
484
|
+
'proc-realtime',
|
|
485
|
+
'session-stream'
|
|
486
|
+
);
|
|
480
487
|
|
|
481
488
|
expect(stream).toBeInstanceOf(ReadableStream);
|
|
482
489
|
|
|
@@ -506,13 +513,13 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
506
513
|
code: 'PROCESS_NOT_FOUND'
|
|
507
514
|
};
|
|
508
515
|
|
|
509
|
-
mockFetch.mockResolvedValue(
|
|
510
|
-
JSON.stringify(errorResponse),
|
|
511
|
-
|
|
512
|
-
));
|
|
516
|
+
mockFetch.mockResolvedValue(
|
|
517
|
+
new Response(JSON.stringify(errorResponse), { status: 404 })
|
|
518
|
+
);
|
|
513
519
|
|
|
514
|
-
await expect(
|
|
515
|
-
.
|
|
520
|
+
await expect(
|
|
521
|
+
client.streamProcessLogs('stream-missing', 'session-err')
|
|
522
|
+
).rejects.toThrow(ProcessNotFoundError);
|
|
516
523
|
});
|
|
517
524
|
|
|
518
525
|
it('should handle streaming setup failures', async () => {
|
|
@@ -521,23 +528,26 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
521
528
|
code: 'PROCESS_ERROR'
|
|
522
529
|
};
|
|
523
530
|
|
|
524
|
-
mockFetch.mockResolvedValue(
|
|
525
|
-
JSON.stringify(errorResponse),
|
|
526
|
-
|
|
527
|
-
));
|
|
531
|
+
mockFetch.mockResolvedValue(
|
|
532
|
+
new Response(JSON.stringify(errorResponse), { status: 500 })
|
|
533
|
+
);
|
|
528
534
|
|
|
529
|
-
await expect(
|
|
530
|
-
.
|
|
535
|
+
await expect(
|
|
536
|
+
client.streamProcessLogs('proc-no-logs', 'session-err')
|
|
537
|
+
).rejects.toThrow(ProcessError);
|
|
531
538
|
});
|
|
532
539
|
|
|
533
540
|
it('should handle missing stream body', async () => {
|
|
534
|
-
mockFetch.mockResolvedValue(
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
541
|
+
mockFetch.mockResolvedValue(
|
|
542
|
+
new Response(null, {
|
|
543
|
+
status: 200,
|
|
544
|
+
headers: { 'Content-Type': 'text/event-stream' }
|
|
545
|
+
})
|
|
546
|
+
);
|
|
538
547
|
|
|
539
|
-
await expect(
|
|
540
|
-
.
|
|
548
|
+
await expect(
|
|
549
|
+
client.streamProcessLogs('proc-empty-stream', 'session-err')
|
|
550
|
+
).rejects.toThrow('No response body for streaming');
|
|
541
551
|
});
|
|
542
552
|
});
|
|
543
553
|
|
|
@@ -550,17 +560,19 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
550
560
|
command: 'echo session-test',
|
|
551
561
|
status: 'running',
|
|
552
562
|
pid: 11111,
|
|
553
|
-
startTime: '2023-01-01T00:00:00Z'
|
|
563
|
+
startTime: '2023-01-01T00:00:00Z'
|
|
554
564
|
},
|
|
555
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
565
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
556
566
|
};
|
|
557
567
|
|
|
558
|
-
mockFetch.mockResolvedValue(
|
|
559
|
-
JSON.stringify(mockResponse),
|
|
560
|
-
|
|
561
|
-
));
|
|
568
|
+
mockFetch.mockResolvedValue(
|
|
569
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
570
|
+
);
|
|
562
571
|
|
|
563
|
-
const result = await client.startProcess(
|
|
572
|
+
const result = await client.startProcess(
|
|
573
|
+
'echo session-test',
|
|
574
|
+
'session-test'
|
|
575
|
+
);
|
|
564
576
|
|
|
565
577
|
expect(result.success).toBe(true);
|
|
566
578
|
|
|
@@ -575,32 +587,44 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
575
587
|
it('should handle multiple simultaneous process operations', async () => {
|
|
576
588
|
mockFetch.mockImplementation((url: string, options: RequestInit) => {
|
|
577
589
|
if (url.includes('/start')) {
|
|
578
|
-
return Promise.resolve(
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
590
|
+
return Promise.resolve(
|
|
591
|
+
new Response(
|
|
592
|
+
JSON.stringify({
|
|
593
|
+
success: true,
|
|
594
|
+
process: {
|
|
595
|
+
id: `proc-${Date.now()}`,
|
|
596
|
+
command: JSON.parse(options.body as string).command,
|
|
597
|
+
status: 'running',
|
|
598
|
+
pid: Math.floor(Math.random() * 90000) + 10000,
|
|
599
|
+
startTime: new Date().toISOString()
|
|
600
|
+
},
|
|
601
|
+
timestamp: new Date().toISOString()
|
|
602
|
+
})
|
|
603
|
+
)
|
|
604
|
+
);
|
|
589
605
|
} else if (url.includes('/list')) {
|
|
590
|
-
return Promise.resolve(
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
606
|
+
return Promise.resolve(
|
|
607
|
+
new Response(
|
|
608
|
+
JSON.stringify({
|
|
609
|
+
success: true,
|
|
610
|
+
processes: [],
|
|
611
|
+
count: 0,
|
|
612
|
+
timestamp: new Date().toISOString()
|
|
613
|
+
})
|
|
614
|
+
)
|
|
615
|
+
);
|
|
596
616
|
} else if (url.includes('/logs')) {
|
|
597
|
-
return Promise.resolve(
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
617
|
+
return Promise.resolve(
|
|
618
|
+
new Response(
|
|
619
|
+
JSON.stringify({
|
|
620
|
+
success: true,
|
|
621
|
+
processId: url.split('/')[4],
|
|
622
|
+
stdout: 'log output',
|
|
623
|
+
stderr: '',
|
|
624
|
+
timestamp: new Date().toISOString()
|
|
625
|
+
})
|
|
626
|
+
)
|
|
627
|
+
);
|
|
604
628
|
}
|
|
605
629
|
return Promise.resolve(new Response('{}', { status: 200 }));
|
|
606
630
|
});
|
|
@@ -610,11 +634,11 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
610
634
|
client.startProcess('python api.py', 'session-concurrent'),
|
|
611
635
|
client.listProcesses('session-concurrent'),
|
|
612
636
|
client.getProcessLogs('existing-proc', 'session-concurrent'),
|
|
613
|
-
client.startProcess('node worker.js', 'session-concurrent')
|
|
637
|
+
client.startProcess('node worker.js', 'session-concurrent')
|
|
614
638
|
]);
|
|
615
639
|
|
|
616
640
|
expect(operations).toHaveLength(5);
|
|
617
|
-
operations.forEach(result => {
|
|
641
|
+
operations.forEach((result) => {
|
|
618
642
|
expect(result.success).toBe(true);
|
|
619
643
|
});
|
|
620
644
|
|
|
@@ -626,18 +650,19 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
626
650
|
it('should handle network failures gracefully', async () => {
|
|
627
651
|
mockFetch.mockRejectedValue(new Error('Network connection failed'));
|
|
628
652
|
|
|
629
|
-
await expect(client.listProcesses('session-err'))
|
|
630
|
-
|
|
653
|
+
await expect(client.listProcesses('session-err')).rejects.toThrow(
|
|
654
|
+
'Network connection failed'
|
|
655
|
+
);
|
|
631
656
|
});
|
|
632
657
|
|
|
633
658
|
it('should handle malformed server responses', async () => {
|
|
634
|
-
mockFetch.mockResolvedValue(
|
|
635
|
-
'invalid json {',
|
|
636
|
-
|
|
637
|
-
));
|
|
659
|
+
mockFetch.mockResolvedValue(
|
|
660
|
+
new Response('invalid json {', { status: 200 })
|
|
661
|
+
);
|
|
638
662
|
|
|
639
|
-
await expect(
|
|
640
|
-
.
|
|
663
|
+
await expect(
|
|
664
|
+
client.startProcess('test-command', 'session-err')
|
|
665
|
+
).rejects.toThrow(SandboxError);
|
|
641
666
|
});
|
|
642
667
|
});
|
|
643
668
|
|
|
@@ -650,9 +675,9 @@ data: {"type":"stdout","data":"Server ready on port 3000\\n","timestamp":"2023-0
|
|
|
650
675
|
it('should initialize with full options', () => {
|
|
651
676
|
const fullOptionsClient = new ProcessClient({
|
|
652
677
|
baseUrl: 'http://custom.com',
|
|
653
|
-
port: 8080
|
|
678
|
+
port: 8080
|
|
654
679
|
});
|
|
655
680
|
expect(fullOptionsClient).toBeDefined();
|
|
656
681
|
});
|
|
657
682
|
});
|
|
658
|
-
});
|
|
683
|
+
});
|