Files
bare-operating-system/packages/bare-os-booter/test.kernel.js
T
2026-08-18 16:43:40 -04:00

1075 lines
28 KiB
JavaScript

import {
test,
b4a,
Hyperdrive,
Corestore,
mkdirSync,
rmSync,
readFile,
readdir,
path,
fileURLToPath,
PassThrough,
XTERM_CLEAR_SCROLLBACK_AND_VIEWPORT,
runKernelFromSource,
runBinCommand,
createGitFsFromVfs,
runGitCli,
createStreamLineReader,
sanitizeInteractiveShellLine,
createVfs,
BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE,
parseHdmsPairArgv,
hcCrypto,
idEnc,
decodeRwShareOffer,
decodeShareOffer,
HDMS_AUTOPASS_SHARE_KEY,
HDMS_AUTOPASS_SHARE_RW_KEY,
normalizeWriterSecretHex,
buildBareOsHypercorePackHrpcLifecycleProcJson,
buildBareOsPearCorestoreHrpcProcJson,
buildBareOsPearInspectLoggerTlsProcJson,
buildBareOsBareRuntimeProtoMuxProcJson,
buildBareOsBootGraphProcJson,
pathnameExpandShellWord,
createBareOsIpc,
BareOsSwarmConnectionManager,
createBareOsLifecycleStateMachine,
runTransactionalLifecycleHooks,
bareOsRegisterCorestoreSuspendResumeHooks,
bareOsInstallCorestoreSuspendResumeHooks,
bareOsFindOrphanProcessRows,
topologicalOrderKernelExtensions,
resolveKernelExtensionsFull,
createBareOsProtomuxAliasRegistry,
bareOsHttpUrlAllowed,
bareOsParseBareAclText,
bareOsAclDeniesSubject,
raceWithAbortAndTimeout,
tokenize,
expandWord,
execShellLine,
expandArgvAliases,
defaultShellAliases,
loadBarerc,
BARERC_SKELETON,
splitTokensBySemicolon,
splitTokensByAndOr,
bareOsShellAstSnapshot,
planShellRedirections,
buildShellExecutionGraph,
dispatchShellTrapSignal,
BARE_OS_EXIT_STATUS_ENV,
syncBareOsExitStatusEnv,
getBareOsPipelineLimits,
DEFAULT_PIPELINE_MAX_STAGES,
tokenizeBareShellLineDetailed,
decodeBareOsDollarQuote,
applyBareOsThemeFromEnv,
bareOsListThemeNames,
bareParseLsColors,
bareSerializeLsColors,
bareLsColorOpenSgrFromMap,
bareDefaultDircolorsDatabase,
bareParseDircolorsDatabase,
BARE_OS_CTX_API_VERSION,
buildBareOsRuntimeCaps,
bareOsBareModulesEnabled,
bareOsBareModuleManifestEmbeddedRef,
buildBareCtxObjectFromHost,
buildPearCtxObjectFromHost,
loadBareModuleManifest,
maybeMergeBareFromDrive,
maybeBareOsBuildBinManifest,
createFishReadLine,
fuzzyMatch,
stripAnsi,
parseHistoryFile,
formatHistoryFile,
dedupeConsecutiveHistory,
searchHistoryEntries,
shouldPersistShellHistoryCommand,
scrubShellHistoryEntries,
SHELL_BUILTINS,
resolveShellPromptHookSegment,
parseCompletionContext,
rankCompletionItems,
suggestGhostFromHistory,
suggestGhostFromFs,
ghostReplaceCurrentWord,
longestCommonCompletionPrefix,
completeLine,
levenshtein,
fieldMatches,
dowFieldMatches,
parseCronLine,
jobMatchesDate,
bareInitdReadinessSnapshot,
bareInitdShutdownActiveUnitsReverse,
getBareServiceRuntime,
registerBareInitdDisposer,
registerKernelShutdownHook,
runKernelShutdownHooks,
startBareInitd,
stopBareInitd,
getLastBareInitdDagSnapshotJson,
appendVarLog,
ensureBareOsVarLogTree,
KERNEL_CONSOLE_LOG,
BOOT_LOG,
bareOsKernelMetricsReset,
bareOsKernelMetricsSnapshot,
parseUnitDropInText,
bareOsHyperswarmOptsFromEnv,
bareOsBootJoinPeerHexFromEnv,
bareOsBootInitPrefetchEnabled,
bareOsBootWarmBinDownloadDiffEnabled,
bareOsProcessTableSnapshot,
buildBareOsSyscallsProcJson,
bareOsParseSendmsgScmRights,
wantPosixSocketScmRights,
parseBareOsHrpcAllowlistJson,
bareOsHrpcAllowlistDeniesRoute,
buildBareOsProtomuxExtensionsProcJson,
createBareOsDiskOsBridge,
buildBareOsReplicationLiveSketch,
evaluateBareOsPeerAdmission,
bareOsFormatPeerAdmissionAuditEvent,
bareOsPeerAdmissionAuditRateAllow,
parseDelegateAllowSet,
isDelegateKindAllowed,
bareOsAuditAppend,
bareOsAuditVerifyRows,
bareOsDrainFcntlLockWaiters,
buildBareOsReplicationOperatorSurfaceProcJson,
DEFAULT_CURL_USER_AGENT,
DEFAULT_WGET_USER_AGENT,
filenameFromContentDisposition,
encodeUstarHeader,
runTarCli,
require,
__dirname,
TAR_BLK,
concatTarParts,
headerFromInit,
testCorestoreDir,
testCtx,
personalHomeBacking,
readBuiltBin
} from './test/_helpers.js'
test('kernel_program proc JSON includes program metadata', async (t) => {
const o = buildBareOsHypercorePackHrpcLifecycleProcJson(
'kernel_program',
{ BARE_OS_BOOT_SAFE_MODE: '1' }
)
t.is(o.schema, 3)
t.is(o.program, 'bare-os-kernel-program')
t.ok(o.operatorSketches && typeof o.operatorSketches === 'object')
t.ok(o.hooks && o.hooks.bootSafeMode === true)
})
test('giant_phase_program proc id returns same builder as kernel_program', async (t) => {
const a = buildBareOsHypercorePackHrpcLifecycleProcJson('kernel_program', {})
const b = buildBareOsHypercorePackHrpcLifecycleProcJson('giant_phase_program', {})
t.is(a.program, b.program)
t.is(a.schema, b.schema)
})
test('parseUnitDropInText parses ConditionPathExists and Restart keys', async (t) => {
const d = parseUnitDropInText(
'[Unit]\nConditionPathExists=/tmp/x\nRestart=on-failure\nRestartSec=2\n'
)
t.is(d.conditionPathExists, '/tmp/x')
t.is(d.restart, 'on-failure')
t.is(d.restartSec, 2)
})
test('bareOsIpc.signalProcessGroup pushes bareOsProcessGroupSignal to scoped channels', async (t) => {
const ipc = createBareOsIpc()
ipc.create('c1')
ipc.create('c2')
ipc.assignProcessGroup('c1', 7)
ipc.assignProcessGroup('c2', 7)
const p1 = ipc.takeJson('c1')
const p2 = ipc.takeJson('c2')
const out = ipc.signalProcessGroup(7, 'SIGUSR1')
t.is(out.delivered, 2)
const j1 = await p1
const j2 = await p2
t.is(j1.method, 'bareOsProcessGroupSignal')
t.is(j1.signal, 'SIGUSR1')
t.is(j2.pgid, 7)
const st = ipc.stats()
t.ok(st.processGroups && st.processGroups.byPgid)
t.ok(st.processGroups.byPgid['pgid:7'].includes('c1'))
})
test('kernel ext incremental reload runs only new scripts when hot reload env set', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
/** @type {string[]} */
const extDir = ['01-a.json']
/** @type {string[]} */
const ran = []
const vfsFiles = new Map()
const drive = {
async get(p) {
if (p === '/etc/os-release') return b4a.from('ID=test\n')
if (p === '/etc/motd') return b4a.from('')
if (p === '/etc/bare-os/kernel.ext.d/01-a.json') {
return b4a.from(
JSON.stringify({
id: 'a',
scripts: ['/lib/bare-os/extensions/a.js']
})
)
}
if (p === '/etc/bare-os/kernel.ext.d/02-b.json') {
return b4a.from(
JSON.stringify({
id: 'b',
scripts: ['/lib/bare-os/extensions/b.js']
})
)
}
return null
},
async *readdir(d) {
if (d === '/etc/bare-os/kernel.ext.d') {
for (const x of extDir) yield x
}
}
}
const ctx = {
bareOsSkipRepl: true,
env: {
BARE_OS_BOOT_SKIP_STAGES:
'profile,rc,rc.d,rc.local,kernel.d,onboot,selftest',
BARE_OS_KERNEL_EXT_D_HOT_RELOAD: '1'
},
drive,
b4a,
vfs: {
async readFile(p) {
return vfsFiles.get(p) ?? null
},
async writeFile(p, body) {
vfsFiles.set(
p,
body instanceof Uint8Array ? body : b4a.from(String(body))
)
}
},
console: { log() {}, error() {}, warn() {} },
readLine: async () => null,
async execLine() {
return 'ok'
},
async bareOsRunImageScript(path) {
ran.push(path)
}
}
await runKernelFromSource(src, ctx)
t.alike(ran, ['/lib/bare-os/extensions/a.js'])
t.ok(typeof ctx.bareOsReloadKernelExtDropinsSafe === 'function')
extDir.push('02-b.json')
const out = await ctx.bareOsReloadKernelExtDropinsSafe()
t.ok(out.ok)
t.alike(out.ranScripts, ['/lib/bare-os/extensions/b.js'])
t.alike(ran, [
'/lib/bare-os/extensions/a.js',
'/lib/bare-os/extensions/b.js'
])
const jr = b4a.toString(vfsFiles.get('/run/bare-os/kernel-ext-reload.ndjson'))
t.ok(jr.includes('incremental'))
})
test('kernel.ext.d minCtxApiVersion skips drop-in when ctx API too low', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
/** @type {string[]} */
const ran = []
const drive = {
async get(p) {
if (p === '/etc/os-release') return b4a.from('ID=test\n')
if (p === '/etc/motd') return b4a.from('')
if (p === '/etc/bare-os/kernel.ext.d/01-hi.json') {
return b4a.from(
JSON.stringify({
id: 'hi',
minCtxApiVersion: '99.0.0',
scripts: ['/lib/bare-os/extensions/hi.js']
})
)
}
return null
},
async *readdir(d) {
if (d === '/etc/bare-os/kernel.ext.d') yield '01-hi.json'
}
}
const ctx = {
bareOsSkipRepl: true,
bareOsCtxApiVersion: '1.0.0',
env: {
BARE_OS_BOOT_SKIP_STAGES:
'profile,rc,rc.d,rc.local,kernel.d,onboot,selftest'
},
drive,
b4a,
console: { log() {}, error() {}, warn() {} },
readLine: async () => null,
async execLine() {
return 'ok'
},
async bareOsRunImageScript(path) {
ran.push(path)
}
}
await runKernelFromSource(src, ctx)
t.is(ran.length, 0)
})
test('kernel.ext.d conflictsWith strict skips extension scripts', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
/** @type {string[]} */
const ran = []
const drive = {
async get(p) {
if (p === '/etc/os-release') return b4a.from('ID=test\n')
if (p === '/etc/motd') return b4a.from('')
if (p === '/etc/bare-os/kernel.ext.d/01-a.json') {
return b4a.from(
JSON.stringify({
id: 'a',
conflictsWith: ['b'],
scripts: ['/lib/bare-os/extensions/a.js']
})
)
}
if (p === '/etc/bare-os/kernel.ext.d/02-b.json') {
return b4a.from(
JSON.stringify({
id: 'b',
scripts: ['/lib/bare-os/extensions/b.js']
})
)
}
return null
},
async *readdir(d) {
if (d === '/etc/bare-os/kernel.ext.d') {
yield '01-a.json'
yield '02-b.json'
}
}
}
const ctx = {
bareOsSkipRepl: true,
env: {
BARE_OS_BOOT_SKIP_STAGES:
'profile,rc,rc.d,rc.local,kernel.d,onboot,selftest',
BARE_OS_BOOT_POLICY_STRICT: '1'
},
drive,
b4a,
console: { log() {}, error() {}, warn() {} },
readLine: async () => null,
async execLine() {
return 'ok'
},
async bareOsRunImageScript(p) {
ran.push(p)
}
}
await runKernelFromSource(src, ctx)
t.is(ran.length, 0)
})
test('kernel.ext.d dependency cycle strict skips extension scripts', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
/** @type {string[]} */
const ran = []
const drive = {
async get(p) {
if (p === '/etc/os-release') return b4a.from('ID=test\n')
if (p === '/etc/motd') return b4a.from('')
if (p === '/etc/bare-os/kernel.ext.d/01-a.json') {
return b4a.from(
JSON.stringify({
id: 'a',
dependsOn: ['b'],
scripts: ['/lib/bare-os/extensions/a.js']
})
)
}
if (p === '/etc/bare-os/kernel.ext.d/02-b.json') {
return b4a.from(
JSON.stringify({
id: 'b',
dependsOn: ['a'],
scripts: ['/lib/bare-os/extensions/b.js']
})
)
}
return null
},
async *readdir(d) {
if (d === '/etc/bare-os/kernel.ext.d') {
yield '01-a.json'
yield '02-b.json'
}
}
}
const ctx = {
bareOsSkipRepl: true,
env: {
BARE_OS_BOOT_SKIP_STAGES:
'profile,rc,rc.d,rc.local,kernel.d,onboot,selftest',
BARE_OS_BOOT_POLICY_STRICT: '1'
},
drive,
b4a,
console: { log() {}, error() {}, warn() {} },
readLine: async () => null,
async execLine() {
return 'ok'
},
async bareOsRunImageScript(p) {
ran.push(p)
}
}
await runKernelFromSource(src, ctx)
t.is(ran.length, 0)
})
test('stock kernel BARE_OS_BOOT_SAFE_MODE skips kernel.ext.d scripts', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
let extRan = false
const drive = {
async get(p) {
if (p === '/etc/bare-os/kernel.ext.d/01-a.json') {
return b4a.from(
JSON.stringify({
id: 'a',
scripts: ['/lib/bare-os/extensions/x.js']
})
)
}
return null
},
async *readdir(d) {
if (d === '/etc/bare-os/kernel.ext.d') yield '01-a.json'
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_BOOT_SAFE_MODE: '1' },
drive,
b4a,
console: { log() {}, error() {} },
readLine: async () => null,
async execLine() {
return 'ok'
},
async bareOsRunImageScript() {
extRan = true
}
}
await runKernelFromSource(src, ctx)
t.is(extRan, false)
})
test('boot.policy denySeedRpcMethods merges into kernel env deny list', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const drive = {
async get(p) {
if (p === '/etc/os-release') return b4a.from('ID=test\n')
if (p === '/etc/motd') return b4a.from('')
if (p === '/etc/bare-os/boot.policy.json') {
return b4a.from(
JSON.stringify({
denySeedRpcMethods: ['replication_queue', 'capabilities']
})
)
}
return null
},
async *readdir() {}
}
const ctx = {
bareOsSkipRepl: true,
env: {
BARE_OS_BOOT_POLICY: '1',
BARE_OS_BOOT_SKIP_STAGES:
'profile,rc,rc.d,rc.local,kernel.d,onboot,selftest',
BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS: 'version,replication_queue'
},
drive,
b4a,
console: { log() {}, error() {}, warn() {} },
readLine: async () => null,
async execLine() {
return 'ok'
}
}
await runKernelFromSource(src, ctx)
t.is(
ctx.env.BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS,
'version,replication_queue,capabilities'
)
})
test('runKernelFromSource invokes start(ctx)', async (t) => {
const calls = []
const source = `
async function start(ctx) {
ctx.calls.push('ok')
}
`
await runKernelFromSource(source, { calls })
t.is(calls[0], 'ok')
})
test('stock kernel init.js onboot runs multiple BARE_OS_ONBOOT lines when SKIP_REPL', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const execLines = []
const drive = {
async get() {
return null
},
async *readdir() {
/* empty */
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_ONBOOT: 'echo one\n#c\n echo two' },
drive,
b4a,
console: { log() {}, error() {} },
readLine: async () => null,
async execLine(line) {
execLines.push(String(line).trim())
return 'ok'
}
}
await runKernelFromSource(src, ctx)
t.alike(execLines, ['echo one', 'echo two'])
})
test('stock kernel init.js runs rc.local before onboot', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const execLines = []
const drive = {
async get(p) {
if (p === '/etc/bare-os/rc.local') return b4a.from('echo rc-local')
return null
},
async *readdir() {
/* empty */
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_ONBOOT: 'echo onboot-line' },
drive,
b4a,
console: { log() {}, error() {} },
readLine: async () => null,
async execLine(line) {
execLines.push(String(line).trim())
return 'ok'
}
}
await runKernelFromSource(src, ctx)
t.is(execLines[0], 'echo rc-local')
t.is(execLines[1], 'echo onboot-line')
})
test('stock kernel init.js runs kernel.d after rc.local before onboot', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const execLines = []
const drive = {
async get(p) {
if (p === '/etc/bare-os/rc.local') return b4a.from('echo rc-local')
if (p === '/etc/bare-os/kernel.d/05-k.mod') return b4a.from('echo kmod')
return null
},
async *readdir(dir) {
if (dir === '/etc/bare-os/kernel.d') yield '05-k.mod'
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_ONBOOT: 'echo onboot-line' },
drive,
b4a,
console: { log() {}, error() {} },
readLine: async () => null,
async execLine(line) {
execLines.push(String(line).trim())
return 'ok'
}
}
await runKernelFromSource(src, ctx)
t.is(execLines[0], 'echo rc-local')
t.is(execLines[1], 'echo kmod')
t.is(execLines[2], 'echo onboot-line')
})
test('stock kernel init.js BARE_OS_BOOT_TRACE=json emits boot trace JSON on stderr', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const stderr = []
const drive = {
async get() {
return null
},
async *readdir() {
/* empty */
}
}
const ctx = {
bareOsSkipRepl: false,
env: { BARE_OS_BOOT_TRACE: 'json' },
drive,
b4a,
console: {
log() {},
error(...a) {
stderr.push(a.join(' '))
}
},
readLine: async () => null,
async execLine() {
return 'ok'
}
}
await runKernelFromSource(src, ctx)
const phases = stderr
.map((l) => {
try {
return JSON.parse(l)
} catch {
return null
}
})
.filter(Boolean)
const bootSteps = phases.filter(
(o) => o && typeof o.ms === 'number' && o.type !== 'bootOutput'
)
t.ok(bootSteps.length >= 3)
t.ok(bootSteps.some((o) => o.phase === 'rc.local'))
t.ok(bootSteps.some((o) => o.step === 'rc.local'))
t.ok(bootSteps.some((o) => o.phase === 'kernel.d'))
t.ok(bootSteps.some((o) => o.step === 'kernel.d'))
t.ok(bootSteps.every((o) => o.bootTraceSchemaVersion === 2))
t.ok(bootSteps.every((o) => typeof o.ms === 'number'))
t.ok(
phases.some(
(o) => o && o.type === 'bootOutput' && o.step === 'banner'
),
'bootOutput trace for guest-visible banner'
)
})
test('stock kernel BARE_OS_RC_D_SKIP skips matching rc.d snippets', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const execLines = []
const drive = {
async get(p) {
if (p === '/etc/bare-os/rc.d/05-skip.me')
return b4a.from('echo skipped-should-not-run')
if (p === '/etc/bare-os/rc.d/10-keep') return b4a.from('echo kept')
return null
},
async *readdir(dir) {
if (dir === '/etc/bare-os/rc.d') {
yield '05-skip.me'
yield '10-keep'
}
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_ONBOOT: 'echo tail', BARE_OS_RC_D_SKIP: '05-*' },
drive,
b4a,
console: { log() {}, error() {} },
readLine: async () => null,
async execLine(line) {
execLines.push(String(line).trim())
return 'ok'
}
}
await runKernelFromSource(src, ctx)
t.ok(!execLines.some((l) => l.includes('skipped')))
t.is(execLines[0], 'echo kept')
t.is(execLines[1], 'echo tail')
})
test('stock kernel BARE_OS_BOOT_STRICT stops boot after execLine throw', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
const execLines = []
let exitArg = null
const drive = {
async get(p) {
if (p === '/etc/bare-os/rc')
return b4a.from('echo first\n__throw__\necho third')
return null
},
async *readdir() {
/* empty */
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_BOOT_STRICT: '1' },
drive,
b4a,
console: { log() {}, error() {} },
readLine: async () => null,
requestBooterExit(code) {
exitArg = code
},
async execLine(line) {
const t0 = String(line).trim()
if (t0 === '__throw__') throw new Error('boot-fail')
execLines.push(t0)
return 'ok'
}
}
await runKernelFromSource(src, ctx)
t.alike(execLines, ['echo first'])
t.is(exitArg, 1)
})
test('stock kernel BARE_OS_BOOT_DRY_RUN skips execLine and reports bootDryRun in boot proc', async (t) => {
const src = await readFile(
path.join(__dirname, '../../kernel/init.js'),
'utf8'
)
/** @type {string[]} */
const errs = []
/** @type {string[]} */
const execCalls = []
const drive = {
async get(p) {
if (p === '/etc/os-release') return b4a.from('ID=test\n')
if (p === '/etc/motd') return b4a.from('')
if (p === '/etc/bare-os/rc') return b4a.from('echo dry-rc\n')
if (p === '/etc/bare-os/onboot') return b4a.from('echo dry-onboot\n')
return null
},
async *readdir() {
/* empty */
}
}
const ctx = {
bareOsSkipRepl: true,
env: { BARE_OS_BOOT_DRY_RUN: '1' },
drive,
b4a,
console: {
log() {},
warn() {},
error: (...a) => errs.push(a.join(' '))
},
readLine: async () => null,
async execLine(line) {
execCalls.push(String(line || ''))
return 'should-not-run'
},
bareOsPublishBootReady(patch) {
this._bootReady = patch
}
}
await runKernelFromSource(src, ctx)
t.is(execCalls.length, 0)
t.ok(
errs.some(
(m) =>
m.includes('bootDryRunExecLine') ||
m.includes('bootDryRunOnboot') ||
m.includes('[boot-dry-run]')
)
)
t.is(ctx._bootReady?.subsystems?.kernel?.programProc?.bootDryRun, true)
})
test('runBinCommand runs /bin helper', async (t) => {
const dir = testCorestoreDir('bin')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('p'))
await drive.ready()
await personal.ready()
await drive.put(
'/bin/hello',
b4a.from(`
async function run(ctx, argv) {
ctx.out.push(argv.join(' '))
}
`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['hello', 'a', 'b'])
t.is(out[0], 'hello a b')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand runs bare hello.js from cwd on personal drive', async (t) => {
const dir = testCorestoreDir('barejs')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pj'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'hello.js'),
b4a.from(`
async function run(ctx, argv) {
ctx.out.push(argv.join(' '))
}
`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['hello.js', 'x', 'y'])
t.is(out[0], 'hello.js x y')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand strips shebang from user script', async (t) => {
const dir = testCorestoreDir('shebang')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('psh'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'x.js'),
b4a.from(`#!/usr/bin/env bare
async function run(ctx) { ctx.out.push('ok') }
`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['./x.js'])
t.is(out[0], 'ok')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand runs top-level user script without run()', async (t) => {
const dir = testCorestoreDir('norun')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pnr'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'plain.js'),
b4a.from(`ctx.out.push('only-top')`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['./plain.js'])
t.is(out[0], 'only-top')
t.is(ctx.exitCode, 0)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand runs top-level then run() when both present', async (t) => {
const dir = testCorestoreDir('bothrun')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pbr'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'hybrid.js'),
b4a.from(`ctx.out.push('first')
async function run(ctx) { ctx.out.push('second') }
`)
)
const out = []
const ctx = testCtx(drive, personal)
ctx.out = out
await runBinCommand(ctx, ['./hybrid.js'])
t.alike(out, ['first', 'second'])
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('runBinCommand user script error is caught and logged', async (t) => {
const dir = testCorestoreDir('throwjs')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('ptj'))
await drive.ready()
await personal.ready()
await personal.put(
personalHomeBacking('/home/user', 'bad.js'),
b4a.from(`async function run() { test() }`)
)
const errs = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: () => {},
error: (...a) => {
errs.push(a.join(' '))
}
}
await runBinCommand(ctx, ['./bad.js'])
t.ok(errs.length > 0)
t.ok(errs.some((e) => /test|not defined|ReferenceError/i.test(e)))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('ls hides dotfiles unless -a', async (t) => {
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
const lsSrc = await readFile(lsPath, 'utf8')
const dir = testCorestoreDir('lsdot')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pls'))
await drive.ready()
await personal.ready()
await drive.put('/bin/ls', b4a.from(lsSrc))
await personal.put(
personalHomeBacking('/home/user', 'shown.txt'),
b4a.from('')
)
await personal.put(personalHomeBacking('/home/user', '.hidden'), b4a.from(''))
const lines = []
const ctx = testCtx(drive, personal)
ctx.console = {
log: (s) => lines.push(String(s)),
error: (...a) => lines.push(a.join(' '))
}
await runBinCommand(ctx, ['ls'])
const flat = lines.join('\n')
t.ok(flat.includes('shown.txt'))
t.ok(!flat.includes('hidden'))
lines.length = 0
await runBinCommand(ctx, ['ls', '-a'])
const flatA = lines.join('\n')
t.ok(flatA.includes('hidden'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('createStreamLineReader yields lines after newline', async (t) => {
const stdin = new PassThrough()
const chunks = []
const stdout = {
write(s) {
chunks.push(String(s))
}
}
const readLine = createStreamLineReader(stdin, stdout)
const p = readLine('> ')
stdin.write('help\n')
t.is(await p, 'help')
t.ok(chunks.join('').includes('> '))
stdin.end()
})
test('createStreamLineReader yields lines after carriage return (SSH PTY)', async (t) => {
const stdin = new PassThrough()
const stdout = { write() {} }
const readLine = createStreamLineReader(stdin, stdout)
const p = readLine('> ')
stdin.write('pwd\r')
t.is(await p, 'pwd')
const p2 = readLine('> ')
stdin.write('a\r\n')
t.is(await p2, 'a')
stdin.end()
})
test('sanitizeInteractiveShellLine applies DEL and strips controls', async (t) => {
t.is(sanitizeInteractiveShellLine('ls'), 'ls')
t.is(sanitizeInteractiveShellLine('a\x7Fb'), 'b')
t.is(sanitizeInteractiveShellLine('ab\x08'), 'a')
t.is(sanitizeInteractiveShellLine('x\x01y'), 'xy')
})
test('Hyperdrive roundtrips /boot/init.js on Corestore', async (t) => {
const dir = testCorestoreDir('seed')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
await drive.ready()
await drive.put('/boot/init.js', b4a.from('async function start() {}'))
const buf = await drive.get('/boot/init.js')
t.ok(buf)
t.is(b4a.toString(buf), 'async function start() {}')
await drive.close()
rmSync(dir, { recursive: true, force: true })
})
test('Hyperdrive checkout read-only boot probe', async (t) => {
const dir = testCorestoreDir('seed-checkout-ro')
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
await drive.ready()
await drive.put('/boot/init.js', b4a.from('async function start() { return 1 }'))
const version = drive.version
const ro = drive.checkout(version, { writable: false })
const buf = await ro.get('/boot/init.js')
t.ok(buf)
t.is(b4a.toString(buf), 'async function start() { return 1 }')
await t.exception(async () => {
await ro.put('/boot/init.js', b4a.from('mutate'))
})
await ro.close()
await drive.close()
rmSync(dir, { recursive: true, force: true })
})