1+ // Excluded from pnpm test. Use pnpm test:vercel to run these tests.
12import { Sandbox } from "@vercel/sandbox" ;
23import type { ToolExecutionOptions } from "ai" ;
3- import {
4- afterAll ,
5- afterEach ,
6- assert ,
7- beforeAll ,
8- describe ,
9- expect ,
10- it ,
11- } from "vitest" ;
4+ import { afterAll , assert , beforeAll , describe , expect , it } from "vitest" ;
125import { createBashTool } from "./tool.js" ;
136import type { CommandResult } from "./types.js" ;
147
158// AI SDK tool execute requires (args, options) - we provide test options
169const opts : ToolExecutionOptions = { toolCallId : "test" , messages : [ ] } ;
1710
11+ /** Generate a unique test directory to avoid race conditions between tests */
12+ function uniqueDir ( ) : string {
13+ return `/vercel/sandbox/test-${ Date . now ( ) } -${ Math . random ( )
14+ . toString ( 36 )
15+ . slice ( 2 , 8 ) } `;
16+ }
17+
1818/**
1919 * Integration tests that verify createBashTool works correctly
2020 * with the real @vercel/sandbox environment.
2121 *
2222 * These tests require Vercel OIDC authentication.
2323 * Run with: pnpm test:vercel
2424 *
25- * Note: createBashTool automatically uses /vercel/sandbox as the default
25+ * Note: createBashTool automatically uses /vercel/sandbox/workspace as the default
2626 * destination when a @vercel/sandbox instance is provided.
2727 */
2828describe ( "createBashTool @vercel/sandbox integration" , ( ) => {
@@ -41,19 +41,9 @@ describe("createBashTool @vercel/sandbox integration", () => {
4141 beforeAll ( async ( ) => {
4242 console . log ( "Creating sandbox" ) ;
4343 vercelSandbox = await Sandbox . create ( ) ;
44+ console . log ( "Sandbox created" ) ;
4445 } , 60000 ) ;
4546
46- afterEach ( async ( ) => {
47- if ( vercelSandbox ) {
48- const result = await vercelSandbox . runCommand ( "rm" , [
49- "-rf" ,
50- "/vercel/sandbox/workspace" ,
51- ] ) ;
52- expect ( await result . stderr ( ) ) . toBe ( "" ) ;
53- expect ( result . exitCode ) . toBe ( 0 ) ;
54- }
55- } ) ;
56-
5747 afterAll ( async ( ) => {
5848 if ( vercelSandbox ) {
5949 await vercelSandbox . stop ( ) ;
@@ -62,15 +52,17 @@ describe("createBashTool @vercel/sandbox integration", () => {
6252
6353 describe ( "ls command" , ( ) => {
6454 it ( "ls -la lists files with details" , async ( ) => {
55+ const dest = uniqueDir ( ) ;
6556 const { tools } = await createBashTool ( {
6657 sandbox : vercelSandbox ,
58+ destination : dest ,
6759 files : testFiles ,
6860 } ) ;
6961
7062 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
7163 const result = ( await tools . bash . execute (
7264 { command : "ls -la" } ,
73- opts ,
65+ opts
7466 ) ) as CommandResult ;
7567
7668 expect ( result . exitCode ) . toBe ( 0 ) ;
@@ -80,15 +72,17 @@ describe("createBashTool @vercel/sandbox integration", () => {
8072 } , 30000 ) ;
8173
8274 it ( "ls lists directory contents" , async ( ) => {
75+ const dest = uniqueDir ( ) ;
8376 const { tools } = await createBashTool ( {
8477 sandbox : vercelSandbox ,
78+ destination : dest ,
8579 files : testFiles ,
8680 } ) ;
8781
8882 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
8983 const result = ( await tools . bash . execute (
9084 { command : "ls src" } ,
91- opts ,
85+ opts
9286 ) ) as CommandResult ;
9387
9488 expect ( result . exitCode ) . toBe ( 0 ) ;
@@ -98,15 +92,17 @@ describe("createBashTool @vercel/sandbox integration", () => {
9892
9993 describe ( "find command" , ( ) => {
10094 it ( "find . -name '*.ts' finds TypeScript files" , async ( ) => {
95+ const dest = uniqueDir ( ) ;
10196 const { tools } = await createBashTool ( {
10297 sandbox : vercelSandbox ,
98+ destination : dest ,
10399 files : testFiles ,
104100 } ) ;
105101
106102 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
107103 const result = ( await tools . bash . execute (
108104 { command : "find . -name '*.ts'" } ,
109- opts ,
105+ opts
110106 ) ) as CommandResult ;
111107
112108 expect ( result . exitCode ) . toBe ( 0 ) ;
@@ -117,15 +113,17 @@ describe("createBashTool @vercel/sandbox integration", () => {
117113 } , 30000 ) ;
118114
119115 it ( "find . -name '*.json' finds JSON files" , async ( ) => {
116+ const dest = uniqueDir ( ) ;
120117 const { tools } = await createBashTool ( {
121118 sandbox : vercelSandbox ,
119+ destination : dest ,
122120 files : testFiles ,
123121 } ) ;
124122
125123 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
126124 const result = ( await tools . bash . execute (
127125 { command : "find . -name '*.json'" } ,
128- opts ,
126+ opts
129127 ) ) as CommandResult ;
130128
131129 expect ( result . exitCode ) . toBe ( 0 ) ;
@@ -135,15 +133,17 @@ describe("createBashTool @vercel/sandbox integration", () => {
135133
136134 describe ( "grep command" , ( ) => {
137135 it ( "grep -r 'pattern' . searches file contents" , async ( ) => {
136+ const dest = uniqueDir ( ) ;
138137 const { tools } = await createBashTool ( {
139138 sandbox : vercelSandbox ,
139+ destination : dest ,
140140 files : testFiles ,
141141 } ) ;
142142
143143 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
144144 const result = ( await tools . bash . execute (
145145 { command : "grep -r 'export' ." } ,
146- opts ,
146+ opts
147147 ) ) as CommandResult ;
148148
149149 expect ( result . stderr ) . toBe ( "" ) ;
@@ -154,62 +154,69 @@ describe("createBashTool @vercel/sandbox integration", () => {
154154 } , 30000 ) ;
155155
156156 it ( "grep finds specific patterns" , async ( ) => {
157+ const dest = uniqueDir ( ) ;
157158 const { tools } = await createBashTool ( {
158159 sandbox : vercelSandbox ,
160+ destination : dest ,
159161 files : testFiles ,
160162 } ) ;
161163
162164 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
163165 const result = ( await tools . bash . execute (
164166 { command : "grep -r 'hello' ." } ,
165- opts ,
167+ opts
166168 ) ) as CommandResult ;
167169
168170 expect ( result . exitCode ) . toBe ( 0 ) ;
169171 expect ( result . stdout . trim ( ) ) . toBe (
170- './src/index.ts:export const hello = "world";' ,
172+ './src/index.ts:export const hello = "world";'
171173 ) ;
172174 } , 30000 ) ;
173175 } ) ;
174176
175177 describe ( "cat command" , ( ) => {
176178 it ( "cat <file> views file contents" , async ( ) => {
179+ const dest = uniqueDir ( ) ;
177180 const { tools } = await createBashTool ( {
178181 sandbox : vercelSandbox ,
182+ destination : dest ,
179183 files : testFiles ,
180184 } ) ;
181185
182186 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
183187 const result = ( await tools . bash . execute (
184188 { command : "cat src/index.ts" } ,
185- opts ,
189+ opts
186190 ) ) as CommandResult ;
187191
188192 expect ( result . exitCode ) . toBe ( 0 ) ;
189193 expect ( result . stdout ) . toBe ( 'export const hello = "world";' ) ;
190194 } , 30000 ) ;
191195
192196 it ( "cat package.json shows JSON content" , async ( ) => {
197+ const dest = uniqueDir ( ) ;
193198 const { tools } = await createBashTool ( {
194199 sandbox : vercelSandbox ,
200+ destination : dest ,
195201 files : testFiles ,
196202 } ) ;
197203
198204 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
199205 const result = ( await tools . bash . execute (
200206 { command : "cat package.json" } ,
201- opts ,
207+ opts
202208 ) ) as CommandResult ;
203209
204210 expect ( result . exitCode ) . toBe ( 0 ) ;
205211 expect ( result . stdout ) . toBe (
206- '{"name": "test-project", "version": "1.0.0"}' ,
212+ '{"name": "test-project", "version": "1.0.0"}'
207213 ) ;
208214 } , 30000 ) ;
209215 } ) ;
210216
211217 describe ( "working directory" , ( ) => {
212- it ( "pwd shows correct working directory" , async ( ) => {
218+ it ( "uses /vercel/sandbox/workspace as default destination" , async ( ) => {
219+ // This test verifies the default destination behavior - no custom destination
213220 const { tools } = await createBashTool ( {
214221 sandbox : vercelSandbox ,
215222 files : testFiles ,
@@ -218,43 +225,45 @@ describe("createBashTool @vercel/sandbox integration", () => {
218225 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
219226 const result = ( await tools . bash . execute (
220227 { command : "pwd" } ,
221- opts ,
228+ opts
222229 ) ) as CommandResult ;
223230
224231 expect ( result . exitCode ) . toBe ( 0 ) ;
225232 expect ( result . stdout . trim ( ) ) . toBe ( "/vercel/sandbox/workspace" ) ;
226233 } , 30000 ) ;
227234
228235 it ( "pwd shows custom destination within sandbox" , async ( ) => {
229- const customDest = "/vercel/sandbox/project" ;
236+ const dest = uniqueDir ( ) ;
230237 const { tools } = await createBashTool ( {
231238 sandbox : vercelSandbox ,
239+ destination : dest ,
232240 files : testFiles ,
233- destination : customDest ,
234241 } ) ;
235242
236243 assert ( tools . bash . execute , "bash.execute should be defined" ) ;
237244 const result = ( await tools . bash . execute (
238245 { command : "pwd" } ,
239- opts ,
246+ opts
240247 ) ) as CommandResult ;
241248
242249 expect ( result . exitCode ) . toBe ( 0 ) ;
243- expect ( result . stdout . trim ( ) ) . toBe ( customDest ) ;
250+ expect ( result . stdout . trim ( ) ) . toBe ( dest ) ;
244251 } , 30000 ) ;
245252 } ) ;
246253
247254 describe ( "readFile tool" , ( ) => {
248255 it ( "reads file content correctly" , async ( ) => {
256+ const dest = uniqueDir ( ) ;
249257 const { tools } = await createBashTool ( {
250258 sandbox : vercelSandbox ,
259+ destination : dest ,
251260 files : testFiles ,
252261 } ) ;
253262
254263 assert ( tools . readFile . execute , "readFile.execute should be defined" ) ;
255264 const result = ( await tools . readFile . execute (
256- { path : "/vercel/sandbox/workspace/ src/index.ts" } ,
257- opts ,
265+ { path : ` ${ dest } / src/index.ts` } ,
266+ opts
258267 ) ) as { content : string } ;
259268
260269 expect ( result . content ) . toBe ( 'export const hello = "world";' ) ;
@@ -263,8 +272,10 @@ describe("createBashTool @vercel/sandbox integration", () => {
263272
264273 describe ( "writeFile tool" , ( ) => {
265274 it ( "writes file and can be read back" , async ( ) => {
275+ const dest = uniqueDir ( ) ;
266276 const { tools } = await createBashTool ( {
267277 sandbox : vercelSandbox ,
278+ destination : dest ,
268279 files : testFiles ,
269280 } ) ;
270281
@@ -276,12 +287,12 @@ describe("createBashTool @vercel/sandbox integration", () => {
276287 path : "newfile.txt" ,
277288 content : "Hello, World!" ,
278289 } ,
279- opts ,
290+ opts
280291 ) ;
281292
282293 const result = ( await tools . bash . execute (
283294 { command : "cat newfile.txt" } ,
284- opts ,
295+ opts
285296 ) ) as CommandResult ;
286297
287298 expect ( result . stdout ) . toBe ( "Hello, World!" ) ;
@@ -292,8 +303,10 @@ describe("createBashTool @vercel/sandbox integration", () => {
292303
293304 describe ( "promptOptions" , ( ) => {
294305 it ( "uses custom toolPrompt when provided" , async ( ) => {
306+ const dest = uniqueDir ( ) ;
295307 const { tools } = await createBashTool ( {
296308 sandbox : vercelSandbox ,
309+ destination : dest ,
297310 files : { "data.json" : "{}" } ,
298311 promptOptions : {
299312 toolPrompt : "Custom tools: myTool" ,
@@ -305,8 +318,10 @@ describe("createBashTool @vercel/sandbox integration", () => {
305318 } , 30000 ) ;
306319
307320 it ( "disables tool hints with empty string toolPrompt" , async ( ) => {
321+ const dest = uniqueDir ( ) ;
308322 const { tools } = await createBashTool ( {
309323 sandbox : vercelSandbox ,
324+ destination : dest ,
310325 files : { "data.json" : "{}" } ,
311326 promptOptions : {
312327 toolPrompt : "" ,
0 commit comments