1- import { spawn , spawnSync } from 'node:child_process'
2- import path from 'node:path'
31import { afterAll , beforeAll , describe , expect , test } from 'vitest'
42import { Sandbox } from 'e2b'
53import { getUserConfig } from 'src/user'
4+ import {
5+ bufferToText ,
6+ isDebug ,
7+ parseEnvInt ,
8+ runCli ,
9+ runCliWithPipedStdin ,
10+ } from '../../setup'
611
712type UserConfigWithDomain = NonNullable < ReturnType < typeof getUserConfig > > & {
813 domain ?: string
@@ -16,21 +21,30 @@ const domain =
1621 userConfig ?. domain ||
1722 'e2b.app'
1823const apiKey = process . env . E2B_API_KEY || userConfig ?. teamApiKey
24+ const shouldSkip = ! apiKey || isDebug
25+ const integrationTest = test . skipIf ( shouldSkip )
1926const templateId =
2027 process . env . E2B_CLI_BACKEND_TEMPLATE_ID ||
2128 process . env . E2B_TEMPLATE_ID ||
2229 'base'
23- const isDebug = process . env . E2B_DEBUG !== undefined
24- const hasCreds = Boolean ( apiKey )
25- const shouldSkip = ! hasCreds || isDebug
26- const testIf = test . skipIf ( shouldSkip )
27- const cliPath = path . join ( process . cwd ( ) , 'dist' , 'index.js' )
2830const sandboxTimeoutMs = parseEnvInt (
2931 'E2B_CLI_BACKEND_SANDBOX_TIMEOUT_MS' ,
3032 20_000
3133)
3234const perTestTimeoutMs = parseEnvInt ( 'E2B_CLI_BACKEND_TEST_TIMEOUT_MS' , 30_000 )
3335const spawnTimeoutMs = perTestTimeoutMs
36+ const cliEnv : NodeJS . ProcessEnv = {
37+ ...process . env ,
38+ E2B_DOMAIN : domain ,
39+ E2B_API_KEY : apiKey ,
40+ }
41+
42+ delete cliEnv . E2B_DEBUG
43+
44+ const runCliInSandbox = ( args : string [ ] ) =>
45+ runCli ( args , { timeoutMs : spawnTimeoutMs , env : cliEnv } )
46+ const runCliWithPipeInSandbox = ( args : string [ ] , input : Buffer ) =>
47+ runCliWithPipedStdin ( args , input , { timeoutMs : spawnTimeoutMs , env : cliEnv } )
3448
3549describe ( 'sandbox cli backend integration' , ( ) => {
3650 let sandbox : Sandbox
@@ -57,17 +71,21 @@ describe('sandbox cli backend integration', () => {
5771 }
5872 } , 15_000 )
5973
60- testIf ( 'list shows the sandbox' , { timeout : perTestTimeoutMs } , async ( ) => {
61- const listResult = runCli ( [ 'sandbox' , 'list' , '--format' , 'json' ] )
62- expect ( listResult . status ) . toBe ( 0 )
63- expect ( sandboxExistsInList ( listResult . stdout , sandbox . sandboxId ) ) . toBe ( true )
64- } )
74+ integrationTest (
75+ 'list shows the sandbox' ,
76+ { timeout : perTestTimeoutMs } ,
77+ async ( ) => {
78+ const listResult = runCliInSandbox ( [ 'sandbox' , 'list' , '--format' , 'json' ] )
79+ expect ( listResult . status ) . toBe ( 0 )
80+ expect ( sandboxExistsInList ( listResult . stdout , sandbox . sandboxId ) ) . toBe ( true )
81+ }
82+ )
6583
66- testIf (
84+ integrationTest (
6785 'info shows the sandbox details' ,
6886 { timeout : perTestTimeoutMs } ,
6987 async ( ) => {
70- const infoResult = runCli ( [
88+ const infoResult = runCliInSandbox ( [
7189 'sandbox' ,
7290 'info' ,
7391 sandbox . sandboxId ,
@@ -86,11 +104,11 @@ describe('sandbox cli backend integration', () => {
86104 }
87105 )
88106
89- testIf (
107+ integrationTest (
90108 'exec runs a command without piped stdin' ,
91109 { timeout : perTestTimeoutMs } ,
92110 async ( ) => {
93- const execResult = runCli ( [
111+ const execResult = runCliInSandbox ( [
94112 'sandbox' ,
95113 'exec' ,
96114 sandbox . sandboxId ,
@@ -104,11 +122,11 @@ describe('sandbox cli backend integration', () => {
104122 }
105123 )
106124
107- testIf (
125+ integrationTest (
108126 'exec runs a command with piped stdin' ,
109127 { timeout : perTestTimeoutMs } ,
110128 async ( ) => {
111- const pipedExecResult = await runCliWithPipedStdin (
129+ const pipedExecResult = await runCliWithPipeInSandbox (
112130 [ 'sandbox' , 'exec' , sandbox . sandboxId , '--' , 'sh' , '-lc' , 'wc -c' ] ,
113131 Buffer . from ( 'hello\n' , 'utf8' )
114132 )
@@ -120,27 +138,11 @@ describe('sandbox cli backend integration', () => {
120138 }
121139 )
122140
123- /** Note: removing this test for now because it can be slow to get the logs causing tests to time out */
124- // testIf(
125- // 'logs returns successfully',
126- // { timeout: perTestTimeoutMs },
127- // async () => {
128- // const logsResult = runCli([
129- // 'sandbox',
130- // 'logs',
131- // sandbox.sandboxId,
132- // '--format',
133- // 'json',
134- // ])
135- // expect(logsResult.status).toBe(0)
136- // }
137- // )
138-
139- testIf (
141+ integrationTest (
140142 'metrics returns successfully' ,
141143 { timeout : perTestTimeoutMs } ,
142144 async ( ) => {
143- const metricsResult = runCli ( [
145+ const metricsResult = runCliInSandbox ( [
144146 'sandbox' ,
145147 'metrics' ,
146148 sandbox . sandboxId ,
@@ -151,102 +153,24 @@ describe('sandbox cli backend integration', () => {
151153 }
152154 )
153155
154- testIf (
156+ integrationTest (
155157 'kill removes the sandbox' ,
156158 { timeout : perTestTimeoutMs } ,
157159 async ( ) => {
158- const killResult = runCli ( [ 'sandbox' , 'kill' , sandbox . sandboxId ] )
160+ const killResult = runCliInSandbox ( [ 'sandbox' , 'kill' , sandbox . sandboxId ] )
159161 expect ( killResult . status ) . toBe ( 0 )
160162
161163 await assertSandboxNotListed ( sandbox . sandboxId )
162164 }
163165 )
164166} )
165167
166- function runCli (
167- args : string [ ] ,
168- opts ?: { input ?: string | Buffer }
169- ) : ReturnType < typeof spawnSync > {
170- const env : NodeJS . ProcessEnv = {
171- ...process . env ,
172- E2B_DOMAIN : domain ,
173- E2B_API_KEY : apiKey ,
174- }
175- delete env . E2B_DEBUG
176-
177- return spawnSync ( 'node' , [ cliPath , ...args ] , {
178- env,
179- input : opts ?. input ,
180- encoding : 'utf8' ,
181- timeout : spawnTimeoutMs ,
182- } )
183- }
184-
185- type PipeRunResult = {
186- status : number | null
187- stdout : Buffer
188- stderr : Buffer
189- error ?: Error
190- }
191-
192- function runCliWithPipedStdin (
193- args : string [ ] ,
194- input : Buffer
195- ) : Promise < PipeRunResult > {
196- const env : NodeJS . ProcessEnv = {
197- ...process . env ,
198- E2B_DOMAIN : domain ,
199- E2B_API_KEY : apiKey ,
200- }
201- delete env . E2B_DEBUG
202-
203- return new Promise ( ( resolve ) => {
204- const child = spawn ( 'node' , [ cliPath , ...args ] , {
205- env,
206- stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
207- } )
208-
209- const stdoutChunks : Buffer [ ] = [ ]
210- const stderrChunks : Buffer [ ] = [ ]
211- let childError : Error | undefined
212- let timedOut = false
213-
214- const timer = setTimeout ( ( ) => {
215- timedOut = true
216- child . kill ( )
217- } , spawnTimeoutMs )
218-
219- child . stdout . on ( 'data' , ( chunk ) => stdoutChunks . push ( Buffer . from ( chunk ) ) )
220- child . stderr . on ( 'data' , ( chunk ) => stderrChunks . push ( Buffer . from ( chunk ) ) )
221- child . on ( 'error' , ( err ) => {
222- childError = err
223- } )
224- child . on ( 'close' , ( code ) => {
225- clearTimeout ( timer )
226- const timeoutError = timedOut
227- ? Object . assign ( new Error ( 'CLI command timed out' ) , {
228- code : 'ETIMEDOUT' ,
229- } as NodeJS . ErrnoException )
230- : undefined
231- resolve ( {
232- status : code ,
233- stdout : Buffer . concat ( stdoutChunks ) ,
234- stderr : Buffer . concat ( stderrChunks ) ,
235- error : childError ?? timeoutError ,
236- } )
237- } )
238-
239- child . stdin . write ( input )
240- child . stdin . end ( )
241- } )
242- }
243-
244168async function assertSandboxNotListed ( sandboxId : string ) : Promise < void > {
245169 const retries = 10
246170 const delayMs = 500
247171
248172 for ( let i = 0 ; i < retries ; i ++ ) {
249- const listResult = runCli ( [ 'sandbox' , 'list' , '--format' , 'json' ] )
173+ const listResult = runCliInSandbox ( [ 'sandbox' , 'list' , '--format' , 'json' ] )
250174 if ( listResult . status === 0 ) {
251175 const exists = sandboxExistsInList ( listResult . stdout , sandboxId )
252176 if ( ! exists ) {
@@ -273,22 +197,6 @@ function sandboxExistsInList(
273197 return parsed . some ( ( item ) => item . sandboxId === sandboxId )
274198}
275199
276- function bufferToText ( value : Buffer | string | null | undefined ) : string {
277- if ( ! value ) {
278- return ''
279- }
280- return typeof value === 'string' ? value : value . toString ( 'utf8' )
281- }
282-
283- function parseEnvInt ( name : string , fallback : number ) : number {
284- const raw = process . env [ name ]
285- if ( ! raw ) {
286- return fallback
287- }
288- const parsed = Number . parseInt ( raw , 10 )
289- return Number . isFinite ( parsed ) ? parsed : fallback
290- }
291-
292200function safeGetUserConfig ( ) : ReturnType < typeof getUserConfig > | null {
293201 try {
294202 return getUserConfig ( )
0 commit comments