Files
e2b-dev--e2b/packages/js-sdk/tests/setup.ts
T
2026-07-13 12:47:58 +08:00

168 lines
4.2 KiB
TypeScript

import { test as base, onTestFailed } from 'vitest'
import {
BuildInfo,
LogEntry,
Sandbox,
SandboxOpts,
Template,
TemplateClass,
Volume,
} from '../src'
import { template } from './template'
interface SandboxFixture {
sandbox: Sandbox
template: string
sandboxTestId: string
sandboxOpts: Partial<SandboxOpts>
}
interface VolumeFixture {
volume: Volume
}
interface BuildTemplateFixture {
buildTemplate: (
template: TemplateClass,
options?: { name?: string; skipCache?: boolean },
onBuildLogs?: (logEntry: LogEntry) => void
) => Promise<BuildInfo>
}
async function buildTemplate(
template: TemplateClass,
options?: { name?: string; skipCache?: boolean },
onBuildLogs?: (logEntry: LogEntry) => void
): Promise<BuildInfo> {
const buildName = options?.name || `e2b-test-${generateRandomString()}`
const buildInfo: { templateId?: string; buildId?: string } = {}
const captureLogs = (log: LogEntry) => {
if (log.message.includes('Template created with ID:')) {
const match = log.message.match(
/Template created with ID: ([^,]+), Build ID: (.+)/
)
if (match) {
buildInfo.templateId = match[1]
buildInfo.buildId = match[2]
}
}
onBuildLogs?.(log)
}
try {
return await Template.build(template, buildName, {
cpuCount: 1,
memoryMB: 1024,
skipCache: options?.skipCache,
onBuildLogs: captureLogs,
})
} catch (e) {
console.error(
`\n[BUILD FAILED] name=${buildName}, ` +
`template_id=${buildInfo.templateId}, ` +
`build_id=${buildInfo.buildId}, error=${e}`
)
throw e
}
}
export const sandboxTest = base.extend<SandboxFixture>({
template,
sandboxTestId: [
// eslint-disable-next-line no-empty-pattern
async ({}, use) => {
const id = `test-${generateRandomString()}`
await use(id)
},
{ auto: true },
],
sandboxOpts: {},
sandbox: [
async ({ sandboxTestId, sandboxOpts }, use) => {
const sandbox = await Sandbox.create(template, {
metadata: { sandboxTestId },
...sandboxOpts,
})
onTestFailed(() => {
console.error(`\n[TEST FAILED] Sandbox ID: ${sandbox.sandboxId}`)
})
try {
await use(sandbox)
} finally {
try {
await sandbox.kill()
} catch (err) {
if (!isDebug) {
console.warn(
'Failed to kill sandbox — this is expected if the test runs with local envd.'
)
}
}
}
},
{ auto: false },
],
})
export const buildTemplateTest = base.extend<BuildTemplateFixture>({
buildTemplate: [
// eslint-disable-next-line no-empty-pattern
async ({}, use) => {
await use(buildTemplate)
},
{ auto: true },
],
})
export const volumeTest = base
.extend<VolumeFixture>({
volume: [
// eslint-disable-next-line no-empty-pattern
async ({}, use) => {
const volume = await Volume.create(`test-vol-${generateRandomString()}`)
onTestFailed(() => {
console.error(`\n[TEST FAILED] Volume ID: ${volume.volumeId}`)
})
try {
await use(volume)
} finally {
try {
await Volume.destroy(volume.volumeId)
} catch {
// Ignore cleanup errors
}
}
},
{ auto: false },
],
})
.skipIf(process.env.ENABLE_VOLUME_TESTS === undefined)
export const isDebug = process.env.E2B_DEBUG !== undefined
export const isIntegrationTest = process.env.E2B_INTEGRATION_TEST !== undefined
/** Placeholder API key with a valid format for tests that don't hit the API. */
export const TEST_API_KEY = `e2b_${'0'.repeat(40)}`
function generateRandomString(length: number = 8): string {
return Math.random()
.toString(36)
.substring(2, length + 2)
}
export async function wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
/**
* Returns the API URL for the given path, using E2B_DOMAIN env var.
* Supports msw path parameters like :templateID
*/
export function apiUrl(path: string): string {
const domain = process.env.E2B_DOMAIN || 'e2b.app'
return `https://api.${domain}${path}`
}
export { template }