@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
package/tests/git-client.test.ts
CHANGED
|
@@ -19,13 +19,13 @@ describe('GitClient', () => {
|
|
|
19
19
|
|
|
20
20
|
beforeEach(() => {
|
|
21
21
|
vi.clearAllMocks();
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
mockFetch = vi.fn();
|
|
24
24
|
global.fetch = mockFetch as unknown as typeof fetch;
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
client = new GitClient({
|
|
27
27
|
baseUrl: 'http://test.com',
|
|
28
|
-
port: 3000
|
|
28
|
+
port: 3000
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -37,18 +37,24 @@ describe('GitClient', () => {
|
|
|
37
37
|
it('should clone public repositories successfully', async () => {
|
|
38
38
|
const mockResponse: GitCheckoutResponse = {
|
|
39
39
|
success: true,
|
|
40
|
-
stdout:
|
|
40
|
+
stdout:
|
|
41
|
+
"Cloning into 'react'...\nReceiving objects: 100% (1284/1284), done.",
|
|
41
42
|
stderr: '',
|
|
42
43
|
exitCode: 0,
|
|
43
44
|
repoUrl: 'https://github.com/facebook/react.git',
|
|
44
45
|
branch: 'main',
|
|
45
46
|
targetDir: 'react',
|
|
46
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
47
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
47
48
|
};
|
|
48
49
|
|
|
49
|
-
mockFetch.mockResolvedValue(
|
|
50
|
+
mockFetch.mockResolvedValue(
|
|
51
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
52
|
+
);
|
|
50
53
|
|
|
51
|
-
const result = await client.checkout(
|
|
54
|
+
const result = await client.checkout(
|
|
55
|
+
'https://github.com/facebook/react.git',
|
|
56
|
+
'test-session'
|
|
57
|
+
);
|
|
52
58
|
|
|
53
59
|
expect(result.success).toBe(true);
|
|
54
60
|
expect(result.repoUrl).toBe('https://github.com/facebook/react.git');
|
|
@@ -59,16 +65,18 @@ describe('GitClient', () => {
|
|
|
59
65
|
it('should clone repositories to specific branches', async () => {
|
|
60
66
|
const mockResponse: GitCheckoutResponse = {
|
|
61
67
|
success: true,
|
|
62
|
-
stdout:
|
|
68
|
+
stdout: "Cloning into 'project'...\nSwitching to branch 'development'",
|
|
63
69
|
stderr: '',
|
|
64
70
|
exitCode: 0,
|
|
65
71
|
repoUrl: 'https://github.com/company/project.git',
|
|
66
72
|
branch: 'development',
|
|
67
73
|
targetDir: 'project',
|
|
68
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
74
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
69
75
|
};
|
|
70
76
|
|
|
71
|
-
mockFetch.mockResolvedValue(
|
|
77
|
+
mockFetch.mockResolvedValue(
|
|
78
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
79
|
+
);
|
|
72
80
|
|
|
73
81
|
const result = await client.checkout(
|
|
74
82
|
'https://github.com/company/project.git',
|
|
@@ -83,16 +91,18 @@ describe('GitClient', () => {
|
|
|
83
91
|
it('should clone repositories to custom directories', async () => {
|
|
84
92
|
const mockResponse: GitCheckoutResponse = {
|
|
85
93
|
success: true,
|
|
86
|
-
stdout:
|
|
94
|
+
stdout: "Cloning into 'workspace/my-app'...\nDone.",
|
|
87
95
|
stderr: '',
|
|
88
96
|
exitCode: 0,
|
|
89
97
|
repoUrl: 'https://github.com/user/my-app.git',
|
|
90
98
|
branch: 'main',
|
|
91
99
|
targetDir: 'workspace/my-app',
|
|
92
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
100
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
93
101
|
};
|
|
94
102
|
|
|
95
|
-
mockFetch.mockResolvedValue(
|
|
103
|
+
mockFetch.mockResolvedValue(
|
|
104
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
105
|
+
);
|
|
96
106
|
|
|
97
107
|
const result = await client.checkout(
|
|
98
108
|
'https://github.com/user/my-app.git',
|
|
@@ -107,18 +117,24 @@ describe('GitClient', () => {
|
|
|
107
117
|
it('should handle large repository clones with warnings', async () => {
|
|
108
118
|
const mockResponse: GitCheckoutResponse = {
|
|
109
119
|
success: true,
|
|
110
|
-
stdout:
|
|
120
|
+
stdout:
|
|
121
|
+
"Cloning into 'linux'...\nReceiving objects: 100% (8125432/8125432), 2.34 GiB, done.",
|
|
111
122
|
stderr: 'warning: filtering not recognized by server',
|
|
112
123
|
exitCode: 0,
|
|
113
124
|
repoUrl: 'https://github.com/torvalds/linux.git',
|
|
114
125
|
branch: 'master',
|
|
115
126
|
targetDir: 'linux',
|
|
116
|
-
timestamp: '2023-01-01T00:05:30Z'
|
|
127
|
+
timestamp: '2023-01-01T00:05:30Z'
|
|
117
128
|
};
|
|
118
129
|
|
|
119
|
-
mockFetch.mockResolvedValue(
|
|
130
|
+
mockFetch.mockResolvedValue(
|
|
131
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
132
|
+
);
|
|
120
133
|
|
|
121
|
-
const result = await client.checkout(
|
|
134
|
+
const result = await client.checkout(
|
|
135
|
+
'https://github.com/torvalds/linux.git',
|
|
136
|
+
'test-session'
|
|
137
|
+
);
|
|
122
138
|
|
|
123
139
|
expect(result.success).toBe(true);
|
|
124
140
|
expect(result.stderr).toContain('warning:');
|
|
@@ -127,18 +143,23 @@ describe('GitClient', () => {
|
|
|
127
143
|
it('should handle SSH repository URLs', async () => {
|
|
128
144
|
const mockResponse: GitCheckoutResponse = {
|
|
129
145
|
success: true,
|
|
130
|
-
stdout:
|
|
146
|
+
stdout: "Cloning into 'private-project'...\nDone.",
|
|
131
147
|
stderr: '',
|
|
132
148
|
exitCode: 0,
|
|
133
149
|
repoUrl: 'git@github.com:company/private-project.git',
|
|
134
150
|
branch: 'main',
|
|
135
151
|
targetDir: 'private-project',
|
|
136
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
152
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
137
153
|
};
|
|
138
154
|
|
|
139
|
-
mockFetch.mockResolvedValue(
|
|
155
|
+
mockFetch.mockResolvedValue(
|
|
156
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
157
|
+
);
|
|
140
158
|
|
|
141
|
-
const result = await client.checkout(
|
|
159
|
+
const result = await client.checkout(
|
|
160
|
+
'git@github.com:company/private-project.git',
|
|
161
|
+
'test-session'
|
|
162
|
+
);
|
|
142
163
|
|
|
143
164
|
expect(result.success).toBe(true);
|
|
144
165
|
expect(result.repoUrl).toBe('git@github.com:company/private-project.git');
|
|
@@ -149,26 +170,32 @@ describe('GitClient', () => {
|
|
|
149
170
|
const body = JSON.parse(options.body as string);
|
|
150
171
|
const repoName = body.repoUrl.split('/').pop().replace('.git', '');
|
|
151
172
|
|
|
152
|
-
return Promise.resolve(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
173
|
+
return Promise.resolve(
|
|
174
|
+
new Response(
|
|
175
|
+
JSON.stringify({
|
|
176
|
+
success: true,
|
|
177
|
+
stdout: `Cloning into '${repoName}'...\nDone.`,
|
|
178
|
+
stderr: '',
|
|
179
|
+
exitCode: 0,
|
|
180
|
+
repoUrl: body.repoUrl,
|
|
181
|
+
branch: body.branch || 'main',
|
|
182
|
+
targetDir: body.targetDir || repoName,
|
|
183
|
+
timestamp: new Date().toISOString()
|
|
184
|
+
})
|
|
185
|
+
)
|
|
186
|
+
);
|
|
162
187
|
});
|
|
163
188
|
|
|
164
189
|
const operations = await Promise.all([
|
|
165
190
|
client.checkout('https://github.com/facebook/react.git', 'session-1'),
|
|
166
191
|
client.checkout('https://github.com/microsoft/vscode.git', 'session-2'),
|
|
167
|
-
client.checkout('https://github.com/nodejs/node.git', 'session-3', {
|
|
192
|
+
client.checkout('https://github.com/nodejs/node.git', 'session-3', {
|
|
193
|
+
branch: 'v18.x'
|
|
194
|
+
})
|
|
168
195
|
]);
|
|
169
196
|
|
|
170
197
|
expect(operations).toHaveLength(3);
|
|
171
|
-
operations.forEach(result => {
|
|
198
|
+
operations.forEach((result) => {
|
|
172
199
|
expect(result.success).toBe(true);
|
|
173
200
|
});
|
|
174
201
|
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
@@ -177,96 +204,141 @@ describe('GitClient', () => {
|
|
|
177
204
|
|
|
178
205
|
describe('repository error handling', () => {
|
|
179
206
|
it('should handle repository not found errors', async () => {
|
|
180
|
-
mockFetch.mockResolvedValue(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
mockFetch.mockResolvedValue(
|
|
208
|
+
new Response(
|
|
209
|
+
JSON.stringify({
|
|
210
|
+
error: 'Repository not found',
|
|
211
|
+
code: 'GIT_REPOSITORY_NOT_FOUND'
|
|
212
|
+
}),
|
|
213
|
+
{ status: 404 }
|
|
214
|
+
)
|
|
215
|
+
);
|
|
184
216
|
|
|
185
|
-
await expect(
|
|
186
|
-
.
|
|
217
|
+
await expect(
|
|
218
|
+
client.checkout(
|
|
219
|
+
'https://github.com/user/nonexistent.git',
|
|
220
|
+
'test-session'
|
|
221
|
+
)
|
|
222
|
+
).rejects.toThrow(GitRepositoryNotFoundError);
|
|
187
223
|
});
|
|
188
224
|
|
|
189
225
|
it('should handle authentication failures', async () => {
|
|
190
|
-
mockFetch.mockResolvedValue(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
226
|
+
mockFetch.mockResolvedValue(
|
|
227
|
+
new Response(
|
|
228
|
+
JSON.stringify({
|
|
229
|
+
error: 'Authentication failed',
|
|
230
|
+
code: 'GIT_AUTH_FAILED'
|
|
231
|
+
}),
|
|
232
|
+
{ status: 401 }
|
|
233
|
+
)
|
|
234
|
+
);
|
|
194
235
|
|
|
195
|
-
await expect(
|
|
196
|
-
.
|
|
236
|
+
await expect(
|
|
237
|
+
client.checkout(
|
|
238
|
+
'https://github.com/company/private.git',
|
|
239
|
+
'test-session'
|
|
240
|
+
)
|
|
241
|
+
).rejects.toThrow(GitAuthenticationError);
|
|
197
242
|
});
|
|
198
243
|
|
|
199
244
|
it('should handle branch not found errors', async () => {
|
|
200
|
-
mockFetch.mockResolvedValue(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
245
|
+
mockFetch.mockResolvedValue(
|
|
246
|
+
new Response(
|
|
247
|
+
JSON.stringify({
|
|
248
|
+
error: 'Branch not found',
|
|
249
|
+
code: 'GIT_BRANCH_NOT_FOUND'
|
|
250
|
+
}),
|
|
251
|
+
{ status: 404 }
|
|
252
|
+
)
|
|
253
|
+
);
|
|
204
254
|
|
|
205
|
-
await expect(
|
|
206
|
-
'https://github.com/user/repo.git',
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
255
|
+
await expect(
|
|
256
|
+
client.checkout('https://github.com/user/repo.git', 'test-session', {
|
|
257
|
+
branch: 'nonexistent-branch'
|
|
258
|
+
})
|
|
259
|
+
).rejects.toThrow(GitBranchNotFoundError);
|
|
210
260
|
});
|
|
211
261
|
|
|
212
262
|
it('should handle network errors', async () => {
|
|
213
|
-
mockFetch.mockResolvedValue(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
263
|
+
mockFetch.mockResolvedValue(
|
|
264
|
+
new Response(
|
|
265
|
+
JSON.stringify({ error: 'Network error', code: 'GIT_NETWORK_ERROR' }),
|
|
266
|
+
{ status: 503 }
|
|
267
|
+
)
|
|
268
|
+
);
|
|
217
269
|
|
|
218
|
-
await expect(
|
|
219
|
-
.
|
|
270
|
+
await expect(
|
|
271
|
+
client.checkout('https://github.com/user/repo.git', 'test-session')
|
|
272
|
+
).rejects.toThrow(GitNetworkError);
|
|
220
273
|
});
|
|
221
274
|
|
|
222
275
|
it('should handle clone failures', async () => {
|
|
223
|
-
mockFetch.mockResolvedValue(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
276
|
+
mockFetch.mockResolvedValue(
|
|
277
|
+
new Response(
|
|
278
|
+
JSON.stringify({ error: 'Clone failed', code: 'GIT_CLONE_FAILED' }),
|
|
279
|
+
{ status: 507 }
|
|
280
|
+
)
|
|
281
|
+
);
|
|
227
282
|
|
|
228
|
-
await expect(
|
|
229
|
-
.
|
|
283
|
+
await expect(
|
|
284
|
+
client.checkout(
|
|
285
|
+
'https://github.com/large/repository.git',
|
|
286
|
+
'test-session'
|
|
287
|
+
)
|
|
288
|
+
).rejects.toThrow(GitCloneError);
|
|
230
289
|
});
|
|
231
290
|
|
|
232
291
|
it('should handle checkout failures', async () => {
|
|
233
|
-
mockFetch.mockResolvedValue(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
292
|
+
mockFetch.mockResolvedValue(
|
|
293
|
+
new Response(
|
|
294
|
+
JSON.stringify({
|
|
295
|
+
error: 'Checkout failed',
|
|
296
|
+
code: 'GIT_CHECKOUT_FAILED'
|
|
297
|
+
}),
|
|
298
|
+
{ status: 409 }
|
|
299
|
+
)
|
|
300
|
+
);
|
|
237
301
|
|
|
238
|
-
await expect(
|
|
239
|
-
'https://github.com/user/repo.git',
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)
|
|
302
|
+
await expect(
|
|
303
|
+
client.checkout('https://github.com/user/repo.git', 'test-session', {
|
|
304
|
+
branch: 'feature-branch'
|
|
305
|
+
})
|
|
306
|
+
).rejects.toThrow(GitCheckoutError);
|
|
243
307
|
});
|
|
244
308
|
|
|
245
309
|
it('should handle invalid Git URLs', async () => {
|
|
246
|
-
mockFetch.mockResolvedValue(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
310
|
+
mockFetch.mockResolvedValue(
|
|
311
|
+
new Response(
|
|
312
|
+
JSON.stringify({ error: 'Invalid Git URL', code: 'INVALID_GIT_URL' }),
|
|
313
|
+
{ status: 400 }
|
|
314
|
+
)
|
|
315
|
+
);
|
|
250
316
|
|
|
251
|
-
await expect(
|
|
252
|
-
.
|
|
317
|
+
await expect(
|
|
318
|
+
client.checkout('not-a-valid-url', 'test-session')
|
|
319
|
+
).rejects.toThrow(InvalidGitUrlError);
|
|
253
320
|
});
|
|
254
321
|
|
|
255
322
|
it('should handle partial clone failures', async () => {
|
|
256
323
|
const mockResponse: GitCheckoutResponse = {
|
|
257
324
|
success: false,
|
|
258
|
-
stdout:
|
|
325
|
+
stdout: "Cloning into 'repo'...\nReceiving objects: 45% (450/1000)",
|
|
259
326
|
stderr: 'error: RPC failed\nfatal: early EOF',
|
|
260
327
|
exitCode: 128,
|
|
261
328
|
repoUrl: 'https://github.com/problematic/repo.git',
|
|
262
329
|
branch: 'main',
|
|
263
330
|
targetDir: 'repo',
|
|
264
|
-
timestamp: '2023-01-01T00:01:30Z'
|
|
331
|
+
timestamp: '2023-01-01T00:01:30Z'
|
|
265
332
|
};
|
|
266
333
|
|
|
267
|
-
mockFetch.mockResolvedValue(
|
|
334
|
+
mockFetch.mockResolvedValue(
|
|
335
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
336
|
+
);
|
|
268
337
|
|
|
269
|
-
const result = await client.checkout(
|
|
338
|
+
const result = await client.checkout(
|
|
339
|
+
'https://github.com/problematic/repo.git',
|
|
340
|
+
'test-session'
|
|
341
|
+
);
|
|
270
342
|
|
|
271
343
|
expect(result.success).toBe(false);
|
|
272
344
|
expect(result.exitCode).toBe(128);
|
|
@@ -278,35 +350,50 @@ describe('GitClient', () => {
|
|
|
278
350
|
it('should handle network failures', async () => {
|
|
279
351
|
mockFetch.mockRejectedValue(new Error('Network connection failed'));
|
|
280
352
|
|
|
281
|
-
await expect(
|
|
282
|
-
.
|
|
353
|
+
await expect(
|
|
354
|
+
client.checkout('https://github.com/user/repo.git', 'test-session')
|
|
355
|
+
).rejects.toThrow('Network connection failed');
|
|
283
356
|
});
|
|
284
357
|
|
|
285
358
|
it('should handle malformed server responses', async () => {
|
|
286
|
-
mockFetch.mockResolvedValue(
|
|
359
|
+
mockFetch.mockResolvedValue(
|
|
360
|
+
new Response('invalid json {', { status: 200 })
|
|
361
|
+
);
|
|
287
362
|
|
|
288
|
-
await expect(
|
|
289
|
-
.
|
|
363
|
+
await expect(
|
|
364
|
+
client.checkout('https://github.com/user/repo.git', 'test-session')
|
|
365
|
+
).rejects.toThrow(SandboxError);
|
|
290
366
|
});
|
|
291
367
|
|
|
292
368
|
it('should map server errors to client errors', async () => {
|
|
293
369
|
const serverErrorScenarios = [
|
|
294
370
|
{ status: 400, code: 'INVALID_GIT_URL', error: InvalidGitUrlError },
|
|
295
371
|
{ status: 401, code: 'GIT_AUTH_FAILED', error: GitAuthenticationError },
|
|
296
|
-
{
|
|
297
|
-
|
|
372
|
+
{
|
|
373
|
+
status: 404,
|
|
374
|
+
code: 'GIT_REPOSITORY_NOT_FOUND',
|
|
375
|
+
error: GitRepositoryNotFoundError
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
status: 404,
|
|
379
|
+
code: 'GIT_BRANCH_NOT_FOUND',
|
|
380
|
+
error: GitBranchNotFoundError
|
|
381
|
+
},
|
|
298
382
|
{ status: 500, code: 'GIT_OPERATION_FAILED', error: GitError },
|
|
299
|
-
{ status: 503, code: 'GIT_NETWORK_ERROR', error: GitNetworkError }
|
|
383
|
+
{ status: 503, code: 'GIT_NETWORK_ERROR', error: GitNetworkError }
|
|
300
384
|
];
|
|
301
385
|
|
|
302
386
|
for (const scenario of serverErrorScenarios) {
|
|
303
|
-
mockFetch.mockResolvedValueOnce(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
387
|
+
mockFetch.mockResolvedValueOnce(
|
|
388
|
+
new Response(
|
|
389
|
+
JSON.stringify({ error: 'Test error', code: scenario.code }),
|
|
390
|
+
{ status: scenario.status }
|
|
391
|
+
)
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
await expect(
|
|
395
|
+
client.checkout('https://github.com/test/repo.git', 'test-session')
|
|
396
|
+
).rejects.toThrow(scenario.error);
|
|
310
397
|
}
|
|
311
398
|
});
|
|
312
399
|
});
|
|
@@ -320,7 +407,7 @@ describe('GitClient', () => {
|
|
|
320
407
|
it('should initialize with full options', () => {
|
|
321
408
|
const fullOptionsClient = new GitClient({
|
|
322
409
|
baseUrl: 'http://custom.com',
|
|
323
|
-
port: 8080
|
|
410
|
+
port: 8080
|
|
324
411
|
});
|
|
325
412
|
expect(fullOptionsClient).toBeInstanceOf(GitClient);
|
|
326
413
|
});
|