Files
2026-07-13 12:47:58 +08:00

140 lines
3.8 KiB
TypeScript

import { assert, expect, test } from 'vitest'
import { InvalidArgumentError, Sandbox } from '../../src'
import { isDebug, template, wait } from '../setup.js'
test.skipIf(isDebug)(
'filesystem-only auto-pause cannot be combined with auto-resume',
async () => {
// A filesystem-only auto-pause snapshot can only be resumed explicitly, so
// keepMemory:false with autoResume is rejected client-side.
await expect(
Sandbox.create(template, {
timeoutMs: 3_000,
lifecycle: {
onTimeout: { action: 'pause', keepMemory: false },
autoResume: true,
},
})
).rejects.toThrowError(InvalidArgumentError)
}
)
test.skipIf(isDebug)(
'keepMemory is not allowed when onTimeout action is kill',
async () => {
// The discriminated union forbids keepMemory on `action: 'kill'` at compile
// time (asserted by @ts-expect-error). The runtime guard below additionally
// rejects it for untyped (JS) callers that bypass the type.
await expect(
Sandbox.create(template, {
timeoutMs: 3_000,
lifecycle: {
// @ts-expect-error keepMemory is not allowed with action: 'kill'
onTimeout: { action: 'kill', keepMemory: false },
},
})
).rejects.toThrowError(InvalidArgumentError)
}
)
test.skipIf(isDebug)(
'auto-pause without auto-resume requires connect to wake',
async () => {
const sandbox = await Sandbox.create(template, {
timeoutMs: 3_000,
lifecycle: {
onTimeout: 'pause',
autoResume: false,
},
})
try {
await wait(5_000)
assert.equal((await sandbox.getInfo()).state, 'paused')
assert.isFalse(await sandbox.isRunning())
await sandbox.connect()
assert.equal((await sandbox.getInfo()).state, 'running')
assert.isTrue(await sandbox.isRunning())
} finally {
await sandbox.kill().catch(() => {})
}
},
60_000
)
test.skipIf(isDebug)(
'filesystem-only auto-pause reboots on connect',
async () => {
// keepMemory:false makes the timeout auto-pause filesystem-only, so resuming
// cold-boots the sandbox from disk.
const sandbox = await Sandbox.create(template, {
timeoutMs: 3_000,
lifecycle: { onTimeout: { action: 'pause', keepMemory: false } },
})
try {
const marker = 'auto-pause-fs-only'
await sandbox.files.write('/home/user/auto-pause-marker.txt', marker)
const bootBefore = (
await sandbox.files.read('/proc/sys/kernel/random/boot_id')
).trim()
await wait(5_000)
assert.equal((await sandbox.getInfo()).state, 'paused')
// A filesystem-only snapshot cannot auto-resume on traffic; connect
// resumes it by cold-booting.
await sandbox.connect()
const persisted = (
await sandbox.files.read('/home/user/auto-pause-marker.txt')
).trim()
assert.equal(persisted, marker)
const bootAfter = (
await sandbox.files.read('/proc/sys/kernel/random/boot_id')
).trim()
assert.notEqual(bootAfter, bootBefore)
} finally {
await sandbox.kill().catch(() => {})
}
},
60_000
)
test.skipIf(isDebug)(
'auto-resume wakes paused sandbox on http request',
async () => {
const sandbox = await Sandbox.create(template, {
timeoutMs: 3_000,
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})
try {
await sandbox.commands.run('python3 -m http.server 8000', {
background: true,
})
await wait(5_000)
const url = `https://${sandbox.getHost(8000)}`
const res = await fetch(url, { signal: AbortSignal.timeout(15_000) })
assert.equal(res.status, 200)
assert.equal((await sandbox.getInfo()).state, 'running')
assert.isTrue(await sandbox.isRunning())
} finally {
await sandbox.kill().catch(() => {})
}
},
60_000
)