6574 lines
202 KiB
JavaScript
6574 lines
202 KiB
JavaScript
import test from 'brittle'
|
|
import b4a from 'b4a'
|
|
import Hyperdrive from 'hyperdrive'
|
|
import Corestore from 'corestore'
|
|
import { mkdirSync, rmSync } from 'node:fs'
|
|
import { readFile, readdir } from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { createRequire } from 'node:module'
|
|
import { PassThrough } from 'node:stream'
|
|
import { runKernelFromSource, runBinCommand } from './lib/kernel-runner.js'
|
|
import { createGitFsFromVfs } from './lib/git-fs-adapter.js'
|
|
import { runGitCli } from './lib/git-cli.js'
|
|
import { createStreamLineReader } from './lib/cli-readline.js'
|
|
import {
|
|
createVfs,
|
|
BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE
|
|
} from './lib/vfs.js'
|
|
import { buildBareOsHypercorePackHrpcLifecycleProcJson } from './lib/bare-os-proc-hypercore-pack-hrpc-lifecycle.js'
|
|
import { buildBareOsPearCorestoreHrpcProcJson } from './lib/bare-os-proc-pear-corestore-hrpc.js'
|
|
import { buildBareOsPearInspectLoggerTlsProcJson } from './lib/bare-os-proc-pear-inspect-logger-tls.js'
|
|
import { buildBareOsBareRuntimeProtoMuxProcJson } from './lib/bare-os-proc-bare-runtime-proto-mux.js'
|
|
import { pathnameExpandShellWord } from './lib/shell-glob.js'
|
|
import { createBareOsIpc } from './lib/bare-os-ipc.js'
|
|
import { topologicalOrderKernelExtensions } from './lib/kernel-extension-resolver.js'
|
|
import { createBareOsProtomuxAliasRegistry } from './lib/bare-os-protomux-alias-registry.js'
|
|
import { bareOsHttpUrlAllowed } from './lib/bare-os-http-policy.js'
|
|
import {
|
|
bareOsParseBareAclText,
|
|
bareOsAclDeniesSubject
|
|
} from './lib/bare-os-vfs-acl-enforce.js'
|
|
import { raceWithAbortAndTimeout } from './lib/bare-os-abort.js'
|
|
import {
|
|
tokenize,
|
|
expandWord,
|
|
execShellLine,
|
|
expandArgvAliases,
|
|
defaultShellAliases,
|
|
loadBarerc,
|
|
BARERC_SKELETON,
|
|
splitTokensBySemicolon,
|
|
splitTokensByAndOr,
|
|
BARE_OS_EXIT_STATUS_ENV,
|
|
syncBareOsExitStatusEnv,
|
|
getBareOsPipelineLimits,
|
|
DEFAULT_PIPELINE_MAX_STAGES,
|
|
} from './lib/shell.js'
|
|
import {
|
|
applyBareOsThemeFromEnv,
|
|
bareOsListThemeNames
|
|
} from './lib/bare-os-theme-presets.js'
|
|
import {
|
|
bareParseLsColors,
|
|
bareSerializeLsColors,
|
|
bareLsColorOpenSgrFromMap,
|
|
bareDefaultDircolorsDatabase,
|
|
bareParseDircolorsDatabase
|
|
} from 'bare-os-lscolors'
|
|
import { BARE_OS_CTX_API_VERSION } from './lib/bare-os-ctx-api.js'
|
|
import { buildBareOsRuntimeCaps } from './lib/bare-os-runtime-caps.js'
|
|
import {
|
|
bareOsBareModulesEnabled,
|
|
bareOsBareModuleManifestEmbeddedRef,
|
|
buildBareCtxObjectFromHost,
|
|
loadBareModuleManifest,
|
|
maybeMergeBareFromDrive
|
|
} from './lib/bare-os-ctx-bare.js'
|
|
import { maybeBareOsBuildBinManifest } from './lib/bare-os-bin-index.js'
|
|
import {
|
|
fuzzyMatch,
|
|
stripAnsi,
|
|
parseHistoryFile,
|
|
formatHistoryFile,
|
|
dedupeConsecutiveHistory,
|
|
searchHistoryEntries
|
|
} from './lib/fish-readline.js'
|
|
import {
|
|
fieldMatches,
|
|
dowFieldMatches,
|
|
parseCronLine,
|
|
jobMatchesDate
|
|
} from './lib/bare-cron.js'
|
|
import {
|
|
bareInitdReadinessSnapshot,
|
|
bareInitdShutdownActiveUnitsReverse,
|
|
getBareServiceRuntime,
|
|
registerBareInitdDisposer,
|
|
registerKernelShutdownHook,
|
|
runKernelShutdownHooks,
|
|
startBareInitd,
|
|
stopBareInitd,
|
|
getLastBareInitdDagSnapshotJson
|
|
} from './lib/bare-initd.js'
|
|
import { parseUnitDropInText } from './lib/bare-initd-user.js'
|
|
import { bareOsProcessTableSnapshot } from './lib/bare-os-process-table.js'
|
|
import { buildBareOsSyscallsProcJson } from './lib/bare-os-syscalls-proc-json.js'
|
|
import {
|
|
bareOsParseSendmsgScmRights,
|
|
wantPosixSocketScmRights
|
|
} from './lib/bare-os-socket-scm-rights.js'
|
|
import {
|
|
parseBareOsHrpcAllowlistJson,
|
|
bareOsHrpcAllowlistDeniesRoute
|
|
} from './lib/bare-os-hrpc-allowlist.js'
|
|
import { buildBareOsProtomuxExtensionsProcJson } from './lib/bare-os-protomux-extensions-proc.js'
|
|
import { createBareOsDiskOsBridge } from './lib/bare-os-disk-os-bridge.js'
|
|
import { buildBareOsReplicationLiveSketch } from './lib/bare-os-replication-proc-live.js'
|
|
import { evaluateBareOsPeerAdmission } from './lib/bare-os-peer-admission.js'
|
|
import { buildBareOsReplicationOperatorSurfaceProcJson } from './lib/bare-os-proc-replication-operator-surface.js'
|
|
import {
|
|
DEFAULT_CURL_USER_AGENT,
|
|
DEFAULT_WGET_USER_AGENT
|
|
} from './lib/http-fetch-url.js'
|
|
import { filenameFromContentDisposition } from './lib/curl-cli.js'
|
|
import { encodeUstarHeader, runTarCli } from './lib/tar-cli.js'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
|
|
const TAR_BLK = 512
|
|
|
|
/** @param {Uint8Array[]} parts */
|
|
function concatTarParts(parts) {
|
|
let n = 0
|
|
for (const p of parts) n += p.length
|
|
const out = new Uint8Array(n)
|
|
let o = 0
|
|
for (const p of parts) {
|
|
out.set(p, o)
|
|
o += p.length
|
|
}
|
|
return out
|
|
}
|
|
|
|
function headerFromInit(init, name) {
|
|
if (!init?.headers) return undefined
|
|
const h = init.headers
|
|
if (typeof h.get === 'function') return h.get(name)
|
|
return undefined
|
|
}
|
|
|
|
function testCorestoreDir(name) {
|
|
const dir = path.join(
|
|
__dirname,
|
|
'.test-data',
|
|
name + '-' + process.pid + '-' + Math.random().toString(36).slice(2)
|
|
)
|
|
mkdirSync(path.dirname(dir), { recursive: true })
|
|
return dir
|
|
}
|
|
|
|
function testCtx(drive, personal, env) {
|
|
const shellEnv = {
|
|
HOME: '/home/user',
|
|
PATH: '/bin',
|
|
USER: 'user',
|
|
UID: '1000',
|
|
GID: '1000',
|
|
PWD: '/home/user',
|
|
BARE_OS_EXIT_STATUS: '0',
|
|
...env
|
|
}
|
|
const bareOsIpc = createBareOsIpc()
|
|
const vfs = createVfs(drive, personal, shellEnv, null, { bareOsIpc })
|
|
const ctx = {
|
|
drive,
|
|
personalDrive: personal,
|
|
vfs,
|
|
bareOsIpc,
|
|
env: shellEnv,
|
|
console,
|
|
b4a,
|
|
async bareOsApplyTheme() {
|
|
return applyBareOsThemeFromEnv(this)
|
|
},
|
|
bareOsListThemes() {
|
|
return bareOsListThemeNames()
|
|
}
|
|
}
|
|
return ctx
|
|
}
|
|
|
|
/** Personal-drive path backing logical `/home/<seg>/…` (matches `vfs.js`). */
|
|
function personalHomeBacking(homePath, rel = '') {
|
|
const seg = homePath.replace(/^\/home\//, '').split('/')[0] || 'user'
|
|
const r = String(rel).replace(/^\//, '')
|
|
return r ? `/.bare-os/home/${seg}/${r}` : `/.bare-os/home/${seg}`
|
|
}
|
|
|
|
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, 2)
|
|
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('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('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('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('vfs lstat personal file includes mode uid user mtime', async (t) => {
|
|
const dir = testCorestoreDir('lstatposix')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('lsp'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const vfs = createVfs(sys, personal, {
|
|
HOME: '/home/user',
|
|
PWD: '/home/user',
|
|
PATH: '/bin',
|
|
USER: 'alice',
|
|
UID: '4242',
|
|
GID: '4242'
|
|
})
|
|
await vfs.writeFile('f', b4a.from('x'))
|
|
const st = await vfs.lstat('/home/user/f')
|
|
t.ok(st)
|
|
t.is(st.type, 'file')
|
|
t.is(st.user, 'alice')
|
|
t.is(st.uid, 4242)
|
|
t.ok((st.mode & 0o777) <= 0o777)
|
|
t.ok((st.mode & 0o100000) === 0o100000)
|
|
t.ok(typeof st.mtimeMs === 'number')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs syscalls.json uses schema 8 when provider wired', async (t) => {
|
|
const dir = testCorestoreDir('syscallsproc')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('scp'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const sample = {
|
|
schemaVersion: 8,
|
|
ctxApiVersion: BARE_OS_CTX_API_VERSION,
|
|
ops: ['readFile'],
|
|
errnoHints: { ENOENT: 2 },
|
|
fdModel: { schema: 1, stdio: [0, 1, 2] },
|
|
signalModel: { schema: 1, names: ['INT'] },
|
|
atMs: 0
|
|
}
|
|
const vfs = createVfs(
|
|
sys,
|
|
personal,
|
|
{ HOME: '/home/g', PWD: '/', PATH: '/bin' },
|
|
null,
|
|
{
|
|
procBareOsSyscallsText() {
|
|
return `${JSON.stringify(sample)}\n`
|
|
}
|
|
}
|
|
)
|
|
const buf = await vfs.readFile('/proc/bare_os/syscalls.json')
|
|
const j = JSON.parse(b4a.toString(buf))
|
|
t.is(j.schemaVersion, 8)
|
|
t.ok(j.fdModel && j.fdModel.schema === 1)
|
|
t.ok(j.signalModel && Array.isArray(j.signalModel.names))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /dev/shm named segment write read unlink roundtrip', async (t) => {
|
|
const dir = testCorestoreDir('devshm')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('shm'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const vfs = createVfs(sys, personal, {
|
|
HOME: '/home/g',
|
|
PWD: '/',
|
|
PATH: '/bin'
|
|
})
|
|
const names0 = (await vfs.readdir('/dev')).slice().sort()
|
|
t.ok(names0.includes('shm'))
|
|
await vfs.writeFile('/dev/shm/seg1', b4a.from('abc'))
|
|
const names1 = await vfs.readdir('/dev/shm')
|
|
t.ok(names1.includes('seg1'))
|
|
t.is(b4a.toString(await vfs.readFile('/dev/shm/seg1')), 'abc')
|
|
await vfs.unlink('/dev/shm/seg1')
|
|
const names2 = await vfs.readdir('/dev/shm')
|
|
t.absent(names2.includes('seg1'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /bin warm read cache hit then bareOsClearWarmReadCaches resets', async (t) => {
|
|
const dir = testCorestoreDir('warmcache')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('wc'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
await sys.put('/bin/warmX', b4a.from('payload'))
|
|
const warmRef = { current: null }
|
|
const env = {
|
|
HOME: '/home/g',
|
|
PWD: '/',
|
|
PATH: '/bin',
|
|
BARE_OS_VFS_BIN_CACHE: '1'
|
|
}
|
|
const vfs = createVfs(sys, personal, env, null, {
|
|
warmReadCacheStatsRef: warmRef
|
|
})
|
|
await vfs.readFile('/bin/warmX')
|
|
const h0 = warmRef.current?.hits ?? 0
|
|
await vfs.readFile('/bin/warmX')
|
|
t.ok((warmRef.current?.hits ?? 0) > h0, 'second read should warm-hit')
|
|
vfs.bareOsClearWarmReadCaches()
|
|
t.is(warmRef.current?.hits, 0)
|
|
t.is(warmRef.current?.misses, 0)
|
|
await vfs.readFile('/bin/warmX')
|
|
t.ok((warmRef.current?.misses ?? 0) >= 1, 'after clear, read counts as miss again')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs bareOsEvictLibBareBundlesFromManifest evicts only listed bundle paths', async (t) => {
|
|
const dir = testCorestoreDir('manifestevict')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('me'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
await sys.put('/lib/bare/bundles/b4a.js', b4a.from('b4a'))
|
|
await sys.put('/lib/bare/bundles/safetyCatch.js', b4a.from('sc'))
|
|
const warmRef = { current: null }
|
|
const env = {
|
|
HOME: '/home/g',
|
|
PWD: '/',
|
|
PATH: '/bin',
|
|
BARE_OS_VFS_BIN_CACHE: '1',
|
|
BARE_OS_VFS_LIB_BARE_CACHE: '1'
|
|
}
|
|
const vfs = createVfs(sys, personal, env, null, {
|
|
warmReadCacheStatsRef: warmRef
|
|
})
|
|
await vfs.readFile('/lib/bare/bundles/b4a.js')
|
|
await vfs.readFile('/lib/bare/bundles/safetyCatch.js')
|
|
const hitsBefore = warmRef.current?.hits ?? 0
|
|
await vfs.readFile('/lib/bare/bundles/b4a.js')
|
|
t.ok((warmRef.current?.hits ?? 0) > hitsBefore)
|
|
const manifest = JSON.stringify({
|
|
version: 1,
|
|
entries: [
|
|
{ ctxKey: 'b4a', package: 'b4a', bundle: true },
|
|
{ ctxKey: 'xOther', package: 'x', bundle: false }
|
|
]
|
|
})
|
|
vfs.bareOsEvictLibBareBundlesFromManifest(b4a.from(manifest))
|
|
const missesAfterEvict = warmRef.current?.misses ?? 0
|
|
await vfs.readFile('/lib/bare/bundles/b4a.js')
|
|
t.ok(
|
|
(warmRef.current?.misses ?? 0) > missesAfterEvict,
|
|
'b4a should miss after selective evict'
|
|
)
|
|
const hitsSc = warmRef.current?.hits ?? 0
|
|
await vfs.readFile('/lib/bare/bundles/safetyCatch.js')
|
|
t.ok(
|
|
(warmRef.current?.hits ?? 0) > hitsSc,
|
|
'safetyCatch still served from warm cache'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('blind relay / pairing / geo proc JSON operator-redacted without BARE_OS_PROC_BLIND_PEER_RELAY_HINTS', async (t) => {
|
|
const a = buildBareOsPearCorestoreHrpcProcJson('blind_relay_router', {})
|
|
t.is(a.exposed, false)
|
|
t.is(a.operatorRedacted, true)
|
|
const b = buildBareOsPearInspectLoggerTlsProcJson('blind_pairing_sketch', {})
|
|
t.is(b.exposed, false)
|
|
t.is(b.operatorRedacted, true)
|
|
const c = buildBareOsHypercorePackHrpcLifecycleProcJson('relay_geo_hint', {})
|
|
t.is(c.exposed, false)
|
|
t.is(c.operatorRedacted, true)
|
|
const { buildBareOsBlindRelaySwarmRuntime } = await import(
|
|
'./lib/bare-os-proc-blind-peer-relay-gate.js'
|
|
)
|
|
const rt = {
|
|
swarm: buildBareOsBlindRelaySwarmRuntime(
|
|
{ BARE_OS_SWARM_PROTOMUX_BACKPRESSURE_COUNT: '3' },
|
|
[{ id: 'a' }, { id: 'b' }],
|
|
{
|
|
hasSystemDrive: true,
|
|
hasPersonalDrive: true,
|
|
auxiliaryDriveCount: 0
|
|
}
|
|
)
|
|
}
|
|
t.is(rt.swarm.schema, 2)
|
|
t.is(rt.swarm.replicationSurfaces?.systemDriveOpen, true)
|
|
const on = buildBareOsPearCorestoreHrpcProcJson(
|
|
'blind_relay_router',
|
|
{
|
|
BARE_OS_PROC_BLIND_PEER_RELAY_HINTS: '1',
|
|
BARE_OS_BLIND_RELAY_ROUTER_JSON: '{"lanes":1}',
|
|
BARE_OS_SWARM_PROTOMUX_BACKPRESSURE_COUNT: '3'
|
|
},
|
|
rt
|
|
)
|
|
t.is(on.schema, 2)
|
|
t.is(on.exposed, true)
|
|
t.ok(on.router && typeof on.router === 'object')
|
|
t.is(on.swarm?.peerCount, 2)
|
|
t.is(on.swarm?.relayGeoTier, 'multi_peer')
|
|
t.is(on.swarm?.protomuxBackpressure?.schema, 1)
|
|
t.is(on.swarm?.protomuxBackpressure?.emitCountEstimate, 3)
|
|
const pair = buildBareOsPearInspectLoggerTlsProcJson(
|
|
'blind_pairing_sketch',
|
|
{ BARE_OS_PROC_BLIND_PEER_RELAY_HINTS: 'yes' },
|
|
rt
|
|
)
|
|
t.is(pair.schema, 2)
|
|
t.is(pair.swarm?.pairingSurfaceReady, true)
|
|
const geo = buildBareOsHypercorePackHrpcLifecycleProcJson(
|
|
'relay_geo_hint',
|
|
{ BARE_OS_PROC_BLIND_PEER_RELAY_HINTS: 'true' },
|
|
rt
|
|
)
|
|
t.is(geo.schema, 2)
|
|
t.ok(geo.regions == null)
|
|
t.is(geo.swarm?.relayGeoTier, 'multi_peer')
|
|
})
|
|
|
|
test('pear_runtime_channel proc JSON surfaces env hints', async (t) => {
|
|
const j = buildBareOsPearCorestoreHrpcProcJson('pear_runtime_channel', {
|
|
BARE_OS_PEAR_RUNTIME_CHANNEL: 'dev',
|
|
BARE_OS_PEAR_RUNTIME_STAGE: '2',
|
|
BARE_OS_PEAR_INSPECTOR_ATTACH_HINT: 'off',
|
|
BARE_OS_PEAR_RUNTIME_SNAPSHOT_JSON: '{"schema":1,"ok":true}'
|
|
})
|
|
t.is(j.schema, 1)
|
|
t.is(j.channel, 'dev')
|
|
t.is(j.stage, '2')
|
|
t.is(j.inspectorAttachHint, 'off')
|
|
t.ok(j.snapshot && typeof j.snapshot === 'object')
|
|
})
|
|
|
|
test('protomux_extensions proc JSON gated by BARE_OS_PROC_PROTOMUX_EXTENSIONS_REGISTRY', async (t) => {
|
|
const off = buildBareOsProtomuxExtensionsProcJson({ env: {} })
|
|
t.is(off.exposed, false)
|
|
const on = buildBareOsProtomuxExtensionsProcJson({
|
|
env: { BARE_OS_PROC_PROTOMUX_EXTENSIONS_REGISTRY: '1' },
|
|
registrySnapshot: { aliases: {}, schema: 1 },
|
|
ctxApiVersion: BARE_OS_CTX_API_VERSION
|
|
})
|
|
t.is(on.exposed, true)
|
|
t.is(on.schemaVersion, 2)
|
|
t.is(on.muxWireMajor, 3)
|
|
t.ok(Array.isArray(on.extensionLogicalChannels))
|
|
})
|
|
|
|
test('buildBareOsReplicationLiveSketch reads hyperdrive core lengths', async (t) => {
|
|
const dir = testCorestoreDir('replive-sketch')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('rls'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const sk0 = buildBareOsReplicationLiveSketch(
|
|
{ drive, personalDrive: personal, auxiliaryDrives: [personal] },
|
|
0
|
|
)
|
|
t.is(sk0.schema, 2)
|
|
t.is(sk0.auxiliaryDriveCount, 1)
|
|
t.is(sk0.stallHint, 'no_peers')
|
|
t.ok(typeof sk0.systemCoreLength === 'number')
|
|
const sk1 = buildBareOsReplicationLiveSketch(
|
|
{ drive, personalDrive: personal },
|
|
1
|
|
)
|
|
t.is(sk1.schema, 2)
|
|
t.is(sk1.auxiliaryDriveCount, 0)
|
|
t.is(sk1.stallHint, 'ok')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('protomux_rpc_pool_health proc uses schema 2 and backpressure correlation fields', async (t) => {
|
|
const j = buildBareOsBareRuntimeProtoMuxProcJson('protomux_rpc_pool_health', {
|
|
BARE_OS_PROTOMUX_RPC_POOL_HEALTH_JSON: '{"ok":true}',
|
|
BARE_OS_REPLICATION_BACKPRESSURE_JSON: '{"depth":1}',
|
|
BARE_OS_LAST_PROTOMUX_BACKPRESSURE_MS: '99'
|
|
})
|
|
t.is(j.schema, 2)
|
|
t.ok(j.health)
|
|
t.ok(j.replicationBackpressure)
|
|
t.is(j.lastProtomuxBackpressureMs, 99)
|
|
})
|
|
|
|
test('protomux_rpc_pool_health echoes seed pool hint and wire correlate when env health absent', async (t) => {
|
|
const j = buildBareOsBareRuntimeProtoMuxProcJson(
|
|
'protomux_rpc_pool_health',
|
|
{},
|
|
{
|
|
seedProtomuxRpcPoolHint: { schema: 1, depth: 2 },
|
|
protomuxWireCorrelate: {
|
|
schema: 1,
|
|
peerMuxSessions: 3,
|
|
backpressureEmitCount: 5,
|
|
aliasPairCount: 1,
|
|
bareOsV1WireMessageKinds: 7
|
|
}
|
|
}
|
|
)
|
|
t.is(j.schema, 2)
|
|
t.is(j.health, null)
|
|
t.is(j.seedProtomuxRpcPoolHintEcho?.depth, 2)
|
|
t.is(j.protomuxWireCorrelate?.peerMuxSessions, 3)
|
|
t.is(j.protomuxWireCorrelate?.backpressureEmitCount, 5)
|
|
})
|
|
|
|
test('protomux_channels proc includes wireShapeSummary from runtime hints', async (t) => {
|
|
const j = buildBareOsPearCorestoreHrpcProcJson(
|
|
'protomux_channels',
|
|
{},
|
|
{
|
|
protomuxWireShape: {
|
|
schema: 1,
|
|
bareOsV1MessageKinds: ['read', 'rpc_req'],
|
|
peerMuxSessions: 2
|
|
}
|
|
}
|
|
)
|
|
t.is(j.schema, 1)
|
|
t.is(j.wireShapeSummary?.peerMuxSessions, 2)
|
|
t.ok(Array.isArray(j.wireShapeSummary?.bareOsV1MessageKinds))
|
|
})
|
|
|
|
test('pathnameExpandShellWord caps matches with BARE_OS_GLOB_MAX_MATCHES', async (t) => {
|
|
const names = ['f0', 'f1', 'f2', 'f3', 'f4']
|
|
const vfs = {
|
|
getcwd: () => '/tmp',
|
|
async readdir(d) {
|
|
if (d === '/tmp') return names.slice()
|
|
return []
|
|
},
|
|
async stat(p) {
|
|
if (p === '/tmp') return { type: 'directory' }
|
|
const base = p.replace(/^.*\//, '')
|
|
if (names.includes(base)) return { type: 'file' }
|
|
throw new Error('ENOENT')
|
|
}
|
|
}
|
|
const ctx = { vfs }
|
|
const parts = [{ q: 'u', t: '/tmp/f*' }]
|
|
const out = await pathnameExpandShellWord(ctx, parts, {
|
|
BARE_OS_GLOB_MAX_MATCHES: '2'
|
|
})
|
|
t.is(out.length, 2)
|
|
t.is(out[0], '/tmp/f0')
|
|
t.is(out[1], '/tmp/f1')
|
|
})
|
|
|
|
test('buildBareOsSyscallsProcJson exposes posixLike fd mapping', async (t) => {
|
|
const j = buildBareOsSyscallsProcJson({ ctxApiVersion: BARE_OS_CTX_API_VERSION })
|
|
t.is(j.schemaVersion, 8)
|
|
t.ok(j.fdModel && j.fdModel.posixLike && j.fdModel.posixLike.pipe)
|
|
t.ok(j.fdModel.posixLike.read && j.fdModel.posixLike.write)
|
|
t.ok(j.fdModel.posixLike.fcntl)
|
|
t.ok(j.fdModel.posixLike.poll)
|
|
t.ok(j.fdModel.posixLike.socketFamily)
|
|
t.ok(j.posixXsh && j.posixXsh.schema === 2 && j.posixXsh.namesCsv.includes('open'))
|
|
t.ok(j.posixXsh.namesCsv.includes('poll'))
|
|
t.ok(j.posixXsh.namesCsv.includes('select'))
|
|
t.ok(j.posixXsh.namesCsv.includes('umask'))
|
|
t.ok(j.posixXsh.namesCsv.includes('readv'))
|
|
t.ok(j.posixXsh.namesCsv.includes('writev'))
|
|
t.ok(j.posixXsh.namesCsv.includes('socket'))
|
|
t.ok(j.posixXsh.namesCsv.includes('sendmsg'))
|
|
t.ok(j.posixXsh.namesCsv.includes('recvmsg'))
|
|
t.ok(j.posixXsh.namesCsv.includes('shutdown'))
|
|
t.is(j.socketMsgSurface?.schema, 3)
|
|
t.is(j.socketMsgSurface?.ancillaryControl?.supported, false)
|
|
t.is(j.socketMsgSurface?.ancillaryControl?.errno, 'ENOTSUP')
|
|
t.ok(Array.isArray(j.socketMsgSurface?.ancillaryControl?.rejectKeys))
|
|
const xsh = j.opsDetail.filter((r) => r.posixAlignment)
|
|
t.ok(xsh.length >= 10)
|
|
})
|
|
|
|
test('bareOsParseSendmsgScmRights binary control rejects', async (t) => {
|
|
const r = bareOsParseSendmsgScmRights(
|
|
{ control: new Uint8Array([1]) },
|
|
true,
|
|
4
|
|
)
|
|
t.is(r.ok, false)
|
|
t.is(/** @type {{ reject: string }} */ (r).reject, 'control')
|
|
})
|
|
|
|
test('bareOsParseSendmsgScmRights scm off rejects non-empty fds', async (t) => {
|
|
const r = bareOsParseSendmsgScmRights({ cmsgs: [{ fds: [3] }] }, false, 4)
|
|
t.is(r.ok, false)
|
|
})
|
|
|
|
test('bareOsParseSendmsgScmRights scm on collects fds', async (t) => {
|
|
const r = bareOsParseSendmsgScmRights({ cmsgs: [{ fds: [3, 4] }] }, true, 4)
|
|
t.is(r.ok, true)
|
|
t.is(/** @type {{ fds: number[] }} */ (r).fds.length, 2)
|
|
})
|
|
|
|
test('wantPosixSocketScmRights env', async (t) => {
|
|
t.absent(wantPosixSocketScmRights({}))
|
|
t.ok(wantPosixSocketScmRights({ BARE_OS_POSIX_SOCKET_SCM_RIGHTS: '1' }))
|
|
})
|
|
|
|
test('maybeBareOsBuildBinManifest writes schema 2 with namesDigest', async (t) => {
|
|
/** @type {{ path: string, text: string }[]} */
|
|
const writes = []
|
|
const ctx = {
|
|
env: { BARE_OS_VFS_BIN_INDEX_BUILD: '1' },
|
|
vfs: {
|
|
writeFile: async (p, buf) => {
|
|
writes.push({
|
|
path: String(p),
|
|
text: b4a.toString(buf)
|
|
})
|
|
}
|
|
},
|
|
drive: {
|
|
async *readdir() {
|
|
yield 'b'
|
|
yield 'a'
|
|
}
|
|
},
|
|
b4a,
|
|
console: {}
|
|
}
|
|
await maybeBareOsBuildBinManifest(ctx)
|
|
t.is(writes.length, 1)
|
|
t.is(writes[0].path, '/.bare-os/index/bin-manifest.json')
|
|
const j = JSON.parse(writes[0].text)
|
|
t.is(j.schema, 2)
|
|
t.ok(typeof j.namesDigest === 'string' && j.namesDigest.length === 64)
|
|
t.is(j.names[0], 'a')
|
|
t.is(j.names[1], 'b')
|
|
})
|
|
|
|
test('parseBareOsHrpcAllowlistJson + bareOsHrpcAllowlistDeniesRoute', async (t) => {
|
|
t.is(parseBareOsHrpcAllowlistJson('').allow, null)
|
|
t.is(parseBareOsHrpcAllowlistJson(' ').allow, null)
|
|
const a1 = parseBareOsHrpcAllowlistJson('["kernel.ping"]')
|
|
t.ok(a1.allow && a1.allow.has('kernel.ping'))
|
|
t.ok(bareOsHrpcAllowlistDeniesRoute(a1.allow, 'kernel', 'capabilities'))
|
|
t.absent(bareOsHrpcAllowlistDeniesRoute(a1.allow, 'kernel', 'ping'))
|
|
const a2 = parseBareOsHrpcAllowlistJson('{"kernel.ping":true}')
|
|
t.ok(a2.allow && a2.allow.has('kernel.ping'))
|
|
const a3 = parseBareOsHrpcAllowlistJson('{"*":true}')
|
|
t.ok(a3.allow && a3.allow.has('*'))
|
|
t.absent(bareOsHrpcAllowlistDeniesRoute(a3.allow, 'vfs', 'readText'))
|
|
const a4 = parseBareOsHrpcAllowlistJson('{"kernel.*":true}')
|
|
t.absent(bareOsHrpcAllowlistDeniesRoute(a4.allow, 'kernel', 'capabilities'))
|
|
t.ok(bareOsHrpcAllowlistDeniesRoute(a4.allow, 'vfs', 'readText'))
|
|
t.ok(parseBareOsHrpcAllowlistJson('{').parseError)
|
|
})
|
|
|
|
test('pear_doctor_state proc JSON schema 2 parity fields', async (t) => {
|
|
const j = buildBareOsPearInspectLoggerTlsProcJson(
|
|
'pear_doctor_state',
|
|
{
|
|
BARE_OS_PEAR_DOCTOR_STATE_JSON: '{"ok":true}',
|
|
BARE_OS_PEAR_RUNTIME_VERSION: '1.2.3',
|
|
BARE_OS_PEAR_INSPECT_PROBE: '1',
|
|
BARE_OS_PEAR_DOCTOR_MODULE: 'pear-doctor'
|
|
},
|
|
{}
|
|
)
|
|
t.is(j.schema, 2)
|
|
t.is(j.runtimeVersion, '1.2.3')
|
|
t.is(j.pearInspectProbe, true)
|
|
t.is(j.pearDoctorModuleHint, 'pear-doctor')
|
|
t.ok(j.state && typeof j.state === 'object')
|
|
})
|
|
|
|
test('split suffix rolls past zz into three-letter names', async (t) => {
|
|
const a = 'abcdefghijklmnopqrstuvwxyz'
|
|
function suffix(i) {
|
|
let idx = i
|
|
let len = 2
|
|
let span = 26 ** len
|
|
while (idx >= span) {
|
|
idx -= span
|
|
len++
|
|
span = 26 ** len
|
|
}
|
|
let s = ''
|
|
let n = idx
|
|
for (let p = 0; p < len; p++) {
|
|
s = a[n % 26] + s
|
|
n = Math.floor(n / 26)
|
|
}
|
|
return 'x' + s
|
|
}
|
|
t.is(suffix(0), 'xaa')
|
|
t.is(suffix(675), 'xzz')
|
|
t.is(suffix(676), 'xaaa')
|
|
t.is(suffix(677), 'xaab')
|
|
})
|
|
|
|
test('disk.os replication_snapshot and replication_operator_sketch RPCs', async (t) => {
|
|
const dir = testCorestoreDir('diskosrpc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
await drive.ready()
|
|
const bridge = createBareOsDiskOsBridge({
|
|
drive,
|
|
bareOsIpc: { list: () => [] },
|
|
ctxApiVersion: BARE_OS_CTX_API_VERSION,
|
|
systemRevision: { currentId: 'cur', pendingId: '', slot: 'a' },
|
|
bootStartedMs: Date.now() - 1000,
|
|
seedReplicationStatus: { depth: 1 }
|
|
})
|
|
const snap = await bridge.execRpc('bare_os', 'replication_snapshot', [])
|
|
const sj = JSON.parse(snap)
|
|
t.is(sj.schema, 1)
|
|
t.is(sj.ok, true)
|
|
t.ok(typeof sj.writable === 'boolean')
|
|
const op = await bridge.execRpc('bare_os', 'replication_operator_sketch', [])
|
|
const oj = JSON.parse(op)
|
|
t.is(oj.schema, 4)
|
|
t.is(oj.seedReplicationStatus?.depth, 1)
|
|
t.is(oj.swarmPeerCount, null)
|
|
const badIntent = await bridge.execRpc(
|
|
'bare_os',
|
|
'replication_operator_intent',
|
|
['{}']
|
|
)
|
|
const ij = JSON.parse(badIntent)
|
|
t.is(ij.ok, false)
|
|
t.is(ij.code, 'EPERM')
|
|
await drive.put('/boot/init.js', b4a.from('//x'))
|
|
const pe = await bridge.execRpc('bare_os', 'path_exists', ['/boot/init.js'])
|
|
const pej = JSON.parse(pe)
|
|
t.is(pej.ok, true)
|
|
t.is(pej.exists, true)
|
|
const bi = await bridge.execRpc('bare_os', 'boot_init_exists', [])
|
|
const bij = JSON.parse(bi)
|
|
t.is(bij.ok, true)
|
|
t.is(bij.exists, true)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('disk.os searchLocal merges auxiliary drives and disk_os_hints RPC', async (t) => {
|
|
const dir = testCorestoreDir('diskossearch')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const auxStore = store.namespace('auxs')
|
|
const aux = new Hyperdrive(auxStore)
|
|
await drive.ready()
|
|
await aux.ready()
|
|
await drive.put('/sys/x_only', b4a.from('1'))
|
|
await aux.put('/mirror/y_aux', b4a.from('2'))
|
|
const bridge = createBareOsDiskOsBridge({
|
|
drive,
|
|
auxiliaryDrives: [aux],
|
|
bareOsIpc: { list: () => [] },
|
|
ctxApiVersion: BARE_OS_CTX_API_VERSION,
|
|
systemRevision: null,
|
|
bootStartedMs: Date.now(),
|
|
seedMirrorDriveHintV2: { schema: 2, note: 't' },
|
|
seedHttpDhtProxyHint: { routes: 1 }
|
|
})
|
|
const m = await bridge.searchLocal('y_aux')
|
|
t.ok(m.some((p) => p.includes('y_aux')))
|
|
const m2 = await bridge.searchLocal('x_only')
|
|
t.ok(m2.some((p) => p.includes('x_only')))
|
|
const h = await bridge.execRpc('bare_os', 'disk_os_hints', [])
|
|
const hj = JSON.parse(h)
|
|
t.is(hj.schema, 2)
|
|
t.is(hj.auxiliaryDriveCount, 1)
|
|
t.ok(hj.protocolPackageVersion)
|
|
t.ok(typeof hj.protomuxChannelSchemaVersion === 'number')
|
|
t.is(hj.mirrorDriveHintV2?.schema, 2)
|
|
t.is(hj.httpDhtProxyHint?.routes, 1)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('ls -l long listing uses session user and regular file mode', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const dir = testCorestoreDir('lslong')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pll'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal, {
|
|
USER: 'carol',
|
|
UID: '9001',
|
|
GID: '9001'
|
|
})
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.writeFile('shown.txt', b4a.from(''))
|
|
await runBinCommand(ctx, ['ls', '-l', 'shown.txt'])
|
|
const longLine = lines.find((l) => l.includes('shown.txt'))
|
|
t.ok(longLine)
|
|
t.ok(longLine.includes('carol'))
|
|
t.ok(/^-rw/.test(longLine), 'expected regular file mode prefix')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('ls --color=always colors directory blue and executable green', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const dir = testCorestoreDir('lscolor')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('plsc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.mkdir('subdir', { recursive: true })
|
|
await ctx.vfs.writeFile('xfile', b4a.from(''))
|
|
await ctx.vfs.chmod('xfile', 0o755)
|
|
await runBinCommand(ctx, ['ls', '--color=always'])
|
|
const shortLine = lines.find(
|
|
(l) => l.includes('subdir') && l.includes('xfile')
|
|
)
|
|
t.ok(shortLine)
|
|
t.ok(
|
|
/\x1b\[[0-9;]*msubdir\x1b\[0m/.test(shortLine),
|
|
'directory uses LS_COLORS di SGR'
|
|
)
|
|
t.ok(
|
|
/\x1b\[[0-9;]*mxfile\x1b\[0m/.test(shortLine),
|
|
'executable uses LS_COLORS ex SGR'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('ls honors NO_COLOR over --color=always', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const dir = testCorestoreDir('lscolornc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('plsn'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal, { NO_COLOR: '1' })
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.mkdir('onlydir', { recursive: true })
|
|
await runBinCommand(ctx, ['ls', '--color=always'])
|
|
const shortLine = lines.find((l) => l.includes('onlydir'))
|
|
t.ok(shortLine)
|
|
t.ok(!shortLine.includes('\x1b['), 'NO_COLOR strips ANSI')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('ls rejects unknown long option', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const dir = testCorestoreDir('lsbadopt')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('plbo'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
const errs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: () => {},
|
|
error: (...a) => errs.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['ls', '--not-a-real-option'])
|
|
t.is(ctx.exitCode, 2)
|
|
t.ok(errs.some((e) => e.includes('unrecognized')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs readFile missing path in home does not throw (touch pattern)', async (t) => {
|
|
const dir = testCorestoreDir('vfsreadmiss')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvrm'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const vfs = createVfs(sys, personal, {
|
|
HOME: '/home/zed',
|
|
PWD: '/home/zed',
|
|
PATH: '/bin',
|
|
USER: 'zed',
|
|
UID: '7000',
|
|
GID: '7000'
|
|
})
|
|
const missing = await vfs.readFile('newfile')
|
|
t.is(missing, null)
|
|
await vfs.writeFile('newfile', b4a.from('ok'))
|
|
t.is(b4a.toString(await vfs.readFile('newfile')), 'ok')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs routes HOME to personal drive', async (t) => {
|
|
const dir = testCorestoreDir('vfs')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pv'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = { HOME: '/home/user', PWD: '/home/user', PATH: '/bin' }
|
|
const vfs = createVfs(sys, personal, env)
|
|
await vfs.writeFile('f.txt', b4a.from('hi'))
|
|
const buf = await personal.get(personalHomeBacking('/home/user', 'f.txt'))
|
|
t.ok(buf)
|
|
t.is(b4a.toString(buf), 'hi')
|
|
const sysTry = await sys.get('/home/user/f.txt')
|
|
t.is(sysTry, null)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs routes /.bare to personal drive', async (t) => {
|
|
const dir = testCorestoreDir('vfsbare')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvb'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
await personal.put('/.bare/vault-marker', b4a.from('1'))
|
|
await sys.put('/.bare/system-only', b4a.from('2'))
|
|
const env = { HOME: '/home/user', PWD: '/home/user', PATH: '/bin' }
|
|
const vfs = createVfs(sys, personal, env)
|
|
const names = await vfs.readdir('/.bare')
|
|
t.ok(names.includes('vault-marker'))
|
|
t.is(names.includes('system-only'), false)
|
|
t.is(await sys.get('/.bare/vault-marker'), null)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs chdir / and stat $HOME root (Hyperdrive rejects entry("/"))', async (t) => {
|
|
const dir = testCorestoreDir('vfsroot')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pv2'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = { HOME: '/home/user', PWD: '/home/user', PATH: '/bin' }
|
|
const vfs = createVfs(sys, personal, env)
|
|
await vfs.chdir('/')
|
|
t.is(vfs.getcwd(), '/')
|
|
const st = await vfs.stat('/home/user')
|
|
t.is(st.type, 'directory')
|
|
await vfs.chdir('/home/user')
|
|
t.is(vfs.getcwd(), '/home/user')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs expands ~ and ~/ to $HOME (not read-only /~)', async (t) => {
|
|
const dir = testCorestoreDir('vfstilde')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pv3'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = { HOME: '/home/zuser', PWD: '/home/zuser', PATH: '/bin' }
|
|
const vfs = createVfs(sys, personal, env)
|
|
await vfs.chdir('/')
|
|
await vfs.chdir('~')
|
|
t.is(vfs.getcwd(), '/home/zuser')
|
|
await vfs.writeFile('~/tilde.txt', b4a.from('ok'))
|
|
const buf = await personal.get(
|
|
personalHomeBacking('/home/zuser', 'tilde.txt')
|
|
)
|
|
t.ok(buf)
|
|
t.is(b4a.toString(buf), 'ok')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs lists virtual /home at root; /home shows only active session dir', async (t) => {
|
|
const dir = testCorestoreDir('vfshomevirt')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvh'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const envGuest = { HOME: '/home/guest', PWD: '/home/guest', PATH: '/bin' }
|
|
const vfsG = createVfs(sys, personal, envGuest)
|
|
const rootG = await vfsG.readdir('/')
|
|
t.ok(rootG.includes('home'))
|
|
t.ok(rootG.includes('bin') || rootG.includes('boot') || rootG.length >= 1)
|
|
t.alike(await vfsG.readdir('/home'), ['guest'])
|
|
const envUser = {
|
|
HOME: '/home/eeb18de988e9',
|
|
PWD: '/home/eeb18de988e9',
|
|
PATH: '/bin'
|
|
}
|
|
const vfsU = createVfs(sys, personal, envUser)
|
|
t.alike(await vfsU.readdir('/home'), ['eeb18de988e9'])
|
|
t.is(await vfsU.stat('/home/guest'), null)
|
|
await vfsU.writeFile('/home/eeb18de988e9/x', b4a.from('1'))
|
|
const buf = await personal.get(personalHomeBacking('/home/eeb18de988e9', 'x'))
|
|
t.ok(buf)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /proc /sys read-only pseudo files; write rejected', async (t) => {
|
|
const dir = testCorestoreDir('vfsproc')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvproc'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
USER: 'guest',
|
|
BARE_OS_CTX_API_VERSION: '9.9.9-test'
|
|
}
|
|
const mntMap = new Map()
|
|
const bareOsIpc = createBareOsIpc()
|
|
const vfs = createVfs(
|
|
sys,
|
|
personal,
|
|
env,
|
|
{ getMounts: () => mntMap },
|
|
{
|
|
procSnapshot: { version: '1.2.3-test', cmdline: 'unit-test' },
|
|
bootStartedMs: Date.now() - 4000,
|
|
bootProfileText: () => 'mini\n',
|
|
sessionText: () => 'test-session-id\n',
|
|
initdRunText: () => 'demo-unit\tactive\t1\tdemo\n',
|
|
procBareOsSwarmText: () => '{}\n',
|
|
bareOsIpc
|
|
}
|
|
)
|
|
const root = await vfs.readdir('/')
|
|
t.ok(root.includes('proc'))
|
|
t.ok(root.includes('sys'))
|
|
t.ok(root.includes('tmp'))
|
|
t.ok(root.includes('run'))
|
|
t.ok(root.includes('dev'))
|
|
t.alike(await vfs.readdir('/proc').then((a) => [...a].sort()), [
|
|
'bare_os',
|
|
'bare_os_activity_queue_depth.json',
|
|
'bare_os_async_hooks_lag.json',
|
|
'bare_os_autobase_writer_hint.json',
|
|
'bare_os_autopass_rotation_sketch.json',
|
|
'bare_os_autopass_session_sketch.json',
|
|
'bare_os_bare_addon_policy.json',
|
|
'bare_os_bare_boot_phase_map.json',
|
|
'bare_os_bare_crypto_policy.json',
|
|
'bare_os_bare_daemon_hooks.json',
|
|
'bare_os_bare_diagnostics_channel.json',
|
|
'bare_os_bare_inspect_policy.json',
|
|
'bare_os_bare_ipc_bridge.json',
|
|
'bare_os_bare_kit_bridge.json',
|
|
'bare_os_bare_logger_policy.json',
|
|
'bare_os_bare_module_resolution.json',
|
|
'bare_os_bare_net_interfaces.json',
|
|
'bare_os_bare_pack_cache.json',
|
|
'bare_os_bare_performance_counters.json',
|
|
'bare_os_bare_rpc_registry_sketch.json',
|
|
'bare_os_bare_signals_mask.json',
|
|
'bare_os_bare_signals_profile.json',
|
|
'bare_os_bare_storage_quota.json',
|
|
'bare_os_bare_stream_backpressure.json',
|
|
'bare_os_bare_thread_pool.json',
|
|
'bare_os_bare_timers_budget.json',
|
|
'bare_os_bare_timers_histogram.json',
|
|
'bare_os_bare_tls_session_hint.json',
|
|
'bare_os_bare_vm_sandbox_sketch.json',
|
|
'bare_os_bare_worker_pool.json',
|
|
'bare_os_bare_ws_gateway_sketch.json',
|
|
'bare_os_blind_pairing_sketch.json',
|
|
'bare_os_blind_relay_router.json',
|
|
'bare_os_boot_graph.json',
|
|
'bare_os_bootstrap',
|
|
'bare_os_brittle_snapshot_ci.json',
|
|
'bare_os_broadcast_encryption_hint.json',
|
|
'bare_os_build_attestation_pointer.json',
|
|
'bare_os_bundle_preload_hint.json',
|
|
'bare_os_capabilities',
|
|
'bare_os_capabilities.json',
|
|
'bare_os_cellery_sidecar_hint.json',
|
|
'bare_os_clock.json',
|
|
'bare_os_compact_encoding_profile.json',
|
|
'bare_os_corestore_gc_hint.json',
|
|
'bare_os_debug.json',
|
|
'bare_os_delegate_red.json',
|
|
'bare_os_dht_status.json',
|
|
'bare_os_dns_map_active.json',
|
|
'bare_os_drive_resolve_cache.json',
|
|
'bare_os_drive_version_graph.json',
|
|
'bare_os_extensions.json',
|
|
'bare_os_features',
|
|
'bare_os_form_data_delegate_limits.json',
|
|
'bare_os_form_data_delegate_limits_v2.json',
|
|
'bare_os_giant_phase_program.json',
|
|
'bare_os_gip_transport_sketch.json',
|
|
'bare_os_git_delegate_stats.json',
|
|
'bare_os_git_lfs_budget.json',
|
|
'bare_os_git_lfs_pointer_stats.json',
|
|
'bare_os_hdms_health.json',
|
|
'bare_os_hdms_hints.json',
|
|
'bare_os_host_os.json',
|
|
'bare_os_hrpc_allowlist_sketch.json',
|
|
'bare_os_hrpc_bridge_health.json',
|
|
'bare_os_http_dht_proxy_route.json',
|
|
'bare_os_hyper_multisig_trust_pointer.json',
|
|
'bare_os_hypercore_lengths.json',
|
|
'bare_os_hypercore_repair_hint.json',
|
|
'bare_os_hypercore_replicate_budget.json',
|
|
'bare_os_hypercore_signing_status.json',
|
|
'bare_os_hyperdb_readonly_index.json',
|
|
'bare_os_hyperdrive_sparse_index.json',
|
|
'bare_os_hypermininet_topology.json',
|
|
'bare_os_indexer_catchup.json',
|
|
'bare_os_initd_dag.json',
|
|
'bare_os_initd_graph.json',
|
|
'bare_os_ipc_backpressure.json',
|
|
'bare_os_kernel_program.json',
|
|
'bare_os_libmqjs_queue_depth.json',
|
|
'bare_os_locale.json',
|
|
'bare_os_manifest_hints',
|
|
'bare_os_metrics_live.json',
|
|
'bare_os_metrics_prom.json',
|
|
'bare_os_multisig_quorum_pointer.json',
|
|
'bare_os_net_qos_class.json',
|
|
'bare_os_net_summary.json',
|
|
'bare_os_oidc_publishing_pointer.json',
|
|
'bare_os_pear_api_allowlist_sketch.json',
|
|
'bare_os_pear_appling_manifest.json',
|
|
'bare_os_pear_build_fingerprint.json',
|
|
'bare_os_pear_doctor_state.json',
|
|
'bare_os_pear_drop_events.json',
|
|
'bare_os_pear_ipc_health.json',
|
|
'bare_os_pear_radio_state.json',
|
|
'bare_os_pear_rti_pointer.json',
|
|
'bare_os_pear_runtime_channel.json',
|
|
'bare_os_pear_runtime_matrix.json',
|
|
'bare_os_pear_sidecar_bundle_index.json',
|
|
'bare_os_pear_stage_pointer.json',
|
|
'bare_os_pear_trust.json',
|
|
'bare_os_pear_updater_state.json',
|
|
'bare_os_pear_user_dirs_map.json',
|
|
'bare_os_pear_wakeups_schedule.json',
|
|
'bare_os_pear_workshop_flags.json',
|
|
'bare_os_peer_health',
|
|
'bare_os_protomux_backpressure.json',
|
|
'bare_os_protomux_channel_alias_v2.json',
|
|
'bare_os_protomux_channels.json',
|
|
'bare_os_protomux_extensions.json',
|
|
'bare_os_protomux_rpc_pool_health.json',
|
|
'bare_os_protomux_wire.json',
|
|
'bare_os_provenance',
|
|
'bare_os_quotas',
|
|
'bare_os_react_native_bare_kit.json',
|
|
'bare_os_relay_geo_hint.json',
|
|
'bare_os_replication',
|
|
'bare_os_replication_backpressure.json',
|
|
'bare_os_replication_operator_panel.json',
|
|
'bare_os_resources',
|
|
'bare_os_rlimits.json',
|
|
'bare_os_rocksdb_pointer.json',
|
|
'bare_os_safe_sodium_buffer_policy.json',
|
|
'bare_os_sandbox_profile.json',
|
|
'bare_os_sandbox_worker_queue.json',
|
|
'bare_os_security_context.json',
|
|
'bare_os_security_posture.json',
|
|
'bare_os_seed_handshake',
|
|
'bare_os_session_stats',
|
|
'bare_os_sidecar_resource_cap.json',
|
|
'bare_os_slo_hints.json',
|
|
'bare_os_snapshot_hints',
|
|
'bare_os_snapshot_hints.json',
|
|
'bare_os_staging_slot',
|
|
'bare_os_storage_tier_hint.json',
|
|
'bare_os_structured_clone_budget_v2.json',
|
|
'bare_os_structured_clone_profile.json',
|
|
'bare_os_swarm',
|
|
'bare_os_sync_window.json',
|
|
'bare_os_syscalls',
|
|
'bare_os_syscalls.json',
|
|
'bare_os_udx_extended.json',
|
|
'bare_os_union',
|
|
'bare_os_updater_download_state.json',
|
|
'bare_os_version',
|
|
'bare_os_virtual_registry',
|
|
'bare_os_wave11_operator_slo_v2.json',
|
|
'bare_os_wave11_peer_qos_sketch.json',
|
|
'bare_os_worker_budget.json',
|
|
'cpuinfo',
|
|
'diskstats',
|
|
'loadavg',
|
|
'meminfo',
|
|
'mounts',
|
|
'net',
|
|
'self',
|
|
'uptime',
|
|
'version'
|
|
])
|
|
t.alike(await vfs.readdir('/proc/self').then((a) => [...a].sort()), [
|
|
'cgroups',
|
|
'cmdline',
|
|
'environ',
|
|
'exe',
|
|
'fd',
|
|
'limits'
|
|
])
|
|
const ver = b4a.toString(await vfs.readFile('/proc/version'))
|
|
t.ok(ver.includes('Bare OS'))
|
|
t.ok(ver.includes('bare_os_ctx_api_version=1.2.3-test'))
|
|
const sysVer = b4a.toString(await vfs.readFile('/sys/fs/bare_os/version'))
|
|
t.ok(sysVer.includes('1.2.3-test'))
|
|
const cmd = b4a.toString(await vfs.readFile('/proc/self/cmdline'))
|
|
t.ok(cmd.includes('unit-test'))
|
|
const mem = b4a.toString(await vfs.readFile('/proc/meminfo'))
|
|
t.ok(mem.includes('MemTotal:'))
|
|
const up = b4a.toString(await vfs.readFile('/proc/uptime'))
|
|
t.ok(/^\d+\.\d+\s+\d+\.\d+/.test(up.trim()))
|
|
const cpu = b4a.toString(await vfs.readFile('/proc/cpuinfo'))
|
|
t.ok(cpu.includes('Bare OS pseudo CPU'))
|
|
const la = b4a.toString(await vfs.readFile('/proc/loadavg'))
|
|
t.ok(/^\d+\.\d+\s+\d+\.\d+\s+\d+\.\d+\s+/.test(la))
|
|
const exe = b4a.toString(await vfs.readFile('/proc/self/exe'))
|
|
t.ok(exe.includes('pseudo inode'))
|
|
const cg = b4a.toString(await vfs.readFile('/proc/self/cgroups'))
|
|
t.ok(cg.includes('bare-os'))
|
|
t.alike(await vfs.readdir('/proc/net').then((a) => [...a].sort()), [
|
|
'dev',
|
|
'tcp',
|
|
'udp'
|
|
])
|
|
const tcp = b4a.toString(await vfs.readFile('/proc/net/tcp'))
|
|
t.ok(tcp.includes('local_address'))
|
|
const udp = b4a.toString(await vfs.readFile('/proc/net/udp'))
|
|
t.ok(udp.includes('sl'))
|
|
let mountsTxt = b4a.toString(await vfs.readFile('/proc/mounts'))
|
|
t.ok(mountsTxt.includes('bare-os-root'))
|
|
t.ok(mountsTxt.includes(' / hyperdrive '))
|
|
mntMap.set('vol', { drive: sys, writable: true })
|
|
mountsTxt = b4a.toString(await vfs.readFile('/proc/mounts'))
|
|
t.ok(mountsTxt.includes('/mnt/vol'))
|
|
t.ok(mountsTxt.includes('bare-os-vol'))
|
|
t.alike(await vfs.readdir('/dev').then((a) => [...a].sort()), [
|
|
'null',
|
|
'shm',
|
|
'urandom',
|
|
'zero'
|
|
])
|
|
const ur = await vfs.readFile('/dev/urandom')
|
|
t.ok(ur && ur.byteLength === 4096)
|
|
await vfs.writeFile('/dev/null', b4a.from('gone'))
|
|
t.is((await vfs.readFile('/dev/null'))?.byteLength ?? 0, 0)
|
|
const z = await vfs.readFile('/dev/zero')
|
|
t.ok(z && z.byteLength === 65536)
|
|
t.alike(await vfs.readdir('/run/bare-os').then((a) => [...a].sort()), [
|
|
'boot.json',
|
|
'boot_profile',
|
|
'ipc',
|
|
'ready',
|
|
'session',
|
|
'unit-journal',
|
|
'units',
|
|
'virtual'
|
|
])
|
|
t.alike(await vfs.readdir('/run/bare-os/ipc'), [])
|
|
bareOsIpc.create('q1')
|
|
t.alike(await vfs.readdir('/run/bare-os/ipc'), ['q1'])
|
|
const readP = vfs.readFile('/run/bare-os/ipc/q1')
|
|
const t0 = Date.now()
|
|
await vfs.writeFile('/run/bare-os/ipc/q1', b4a.from('ping'))
|
|
t.is(b4a.toString(await readP), 'ping')
|
|
t.ok(Date.now() - t0 < 60000, 'kernel ipc ping-pong completes')
|
|
await vfs.unlink('/run/bare-os/ipc/q1')
|
|
t.alike(await vfs.readdir('/run/bare-os/ipc'), [])
|
|
t.is(b4a.toString(await vfs.readFile('/run/bare-os/boot_profile')), 'mini\n')
|
|
t.is(
|
|
b4a.toString(await vfs.readFile('/run/bare-os/session')),
|
|
'test-session-id\n'
|
|
)
|
|
const units = b4a.toString(await vfs.readFile('/run/bare-os/units'))
|
|
t.ok(units.includes('demo-unit'))
|
|
let writeErr = null
|
|
try {
|
|
await vfs.writeFile('/proc/version', b4a.from('x'))
|
|
} catch (e) {
|
|
writeErr = e
|
|
}
|
|
t.ok(writeErr)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /proc/mounts lists mirror aux drives', async (t) => {
|
|
const dir = testCorestoreDir('vfsauxmount')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvauxm'))
|
|
const aux = new Hyperdrive(store.namespace('auxmirror'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
await aux.ready()
|
|
const env = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
USER: 'guest'
|
|
}
|
|
const vfs = createVfs(sys, personal, env, { getMounts: () => new Map() }, {
|
|
procBareOsSwarmText: () => '{}\n',
|
|
getAuxiliaryDrives: () => [aux],
|
|
getAuxiliaryMountLines: () => [
|
|
'bare-os-aux0 /mirror/aux0 hyperdrive ro 0 0'
|
|
]
|
|
})
|
|
const mountsTxt = b4a.toString(await vfs.readFile('/proc/mounts'))
|
|
t.ok(mountsTxt.includes('bare-os-aux0'))
|
|
t.ok(mountsTxt.includes('/mirror/aux0'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs BARE_OS_HIDE_PROC_HYPERCORE_PACK_HRPC_LIFECYCLE=0 hides wave11 proc nodes', async (t) => {
|
|
const dir = testCorestoreDir('vfsprocw11')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvprocw11'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
USER: 'guest',
|
|
BARE_OS_HIDE_PROC_HYPERCORE_PACK_HRPC_LIFECYCLE: '0'
|
|
}
|
|
const mntMap = new Map()
|
|
const vfs = createVfs(sys, personal, env, { getMounts: () => mntMap }, {
|
|
procBareOsSwarmText: () => '{}\n'
|
|
})
|
|
const flatW11 = Object.keys(BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE).map((k) =>
|
|
k.endsWith('.json') ? k : `${k}.json`
|
|
)
|
|
const bareOsW11 = Object.keys(BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE).map((k) => {
|
|
const base = k.startsWith('bare_os_') ? k.slice('bare_os_'.length) : k
|
|
return base.endsWith('.json') ? base : `${base}.json`
|
|
})
|
|
const procRoot = await vfs.readdir('/proc').then((a) => [...a])
|
|
for (const n of flatW11) {
|
|
t.ok(!procRoot.includes(n), `flat ${n} should be hidden`)
|
|
}
|
|
const bo = await vfs.readdir('/proc/bare_os').then((a) => [...a])
|
|
for (const n of bareOsW11) {
|
|
t.ok(!bo.includes(n), `bare_os/${n} should be hidden`)
|
|
}
|
|
t.is(
|
|
await vfs.readFile('/proc/bare_os_hypercore_replicate_budget.json'),
|
|
null
|
|
)
|
|
const idxRaw = b4a.toString(await vfs.readFile('/proc/bare_os/index.json'))
|
|
const idx = JSON.parse(idxRaw)
|
|
t.ok(Array.isArray(idx.entries))
|
|
const names = new Set(idx.entries.map((e) => e.name))
|
|
t.ok(!names.has('hypercore_replicate_budget.json'))
|
|
t.ok(!names.has('wave11_peer_qos_sketch.json'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /proc/self/environ omits secret-like env keys', async (t) => {
|
|
const dir = testCorestoreDir('vfsprocenv')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvpe'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
USER: 'guest',
|
|
MY_PASSWORD: 'hunter2',
|
|
BARE_OS_SAFE: 'visible'
|
|
}
|
|
const vfs = createVfs(sys, personal, env)
|
|
const raw = b4a.toString(await vfs.readFile('/proc/self/environ'))
|
|
t.ok(raw.includes('HOME='))
|
|
t.ok(raw.includes('BARE_OS_SAFE='))
|
|
t.ok(!raw.includes('MY_PASSWORD'))
|
|
t.ok(!raw.includes('hunter2'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /tmp maps to personal drive per HOME basename', async (t) => {
|
|
const dir = testCorestoreDir('vfstmp')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvtmp'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const vfsGuest = createVfs(sys, personal, {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin'
|
|
})
|
|
await vfsGuest.writeFile('/tmp/session.dat', b4a.from('g'))
|
|
t.is(b4a.toString(await personal.get('/.bare-os/tmp/guest/session.dat')), 'g')
|
|
const vfsUser = createVfs(sys, personal, {
|
|
HOME: '/home/hexuserdead',
|
|
PWD: '/home/hexuserdead',
|
|
PATH: '/bin'
|
|
})
|
|
t.is(await vfsUser.readFile('/tmp/session.dat'), null)
|
|
await vfsUser.writeFile('/tmp/session.dat', b4a.from('u'))
|
|
t.is(
|
|
b4a.toString(await personal.get('/.bare-os/tmp/hexuserdead/session.dat')),
|
|
'u'
|
|
)
|
|
t.is(b4a.toString(await personal.get('/.bare-os/tmp/guest/session.dat')), 'g')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs isolates home and /var/log per HOME basename on personal drive', async (t) => {
|
|
const dir = testCorestoreDir('vfsisol')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvis'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const vfsGuest = createVfs(sys, personal, {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin'
|
|
})
|
|
await vfsGuest.writeFile('shared-name.txt', b4a.from('guest-data'))
|
|
await vfsGuest.writeFile('/var/log/bare-os/g.log', b4a.from('glog'))
|
|
const vfsUser = createVfs(sys, personal, {
|
|
HOME: '/home/alice12hex',
|
|
PWD: '/home/alice12hex',
|
|
PATH: '/bin'
|
|
})
|
|
t.is(await vfsUser.readFile('/home/alice12hex/shared-name.txt'), null)
|
|
await vfsUser.writeFile('shared-name.txt', b4a.from('user-data'))
|
|
t.is(
|
|
b4a.toString(
|
|
await personal.get(personalHomeBacking('/home/guest', 'shared-name.txt'))
|
|
),
|
|
'guest-data'
|
|
)
|
|
t.is(
|
|
b4a.toString(
|
|
await personal.get(
|
|
personalHomeBacking('/home/alice12hex', 'shared-name.txt')
|
|
)
|
|
),
|
|
'user-data'
|
|
)
|
|
t.is(
|
|
b4a.toString(await personal.get('/.bare-os/var/log/guest/bare-os/g.log')),
|
|
'glog'
|
|
)
|
|
await vfsUser.writeFile('/var/log/bare-os/u.log', b4a.from('ulog'))
|
|
t.is(
|
|
b4a.toString(
|
|
await personal.get('/.bare-os/var/log/alice12hex/bare-os/u.log')
|
|
),
|
|
'ulog'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('guest VFS denies sensitive /.bare paths; unlocked allows', async (t) => {
|
|
const dir = testCorestoreDir('vfsbarepol')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvpol'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
await personal.put('/.bare/account', b4a.from('acctblob'))
|
|
await personal.put('/.bare/vault/x', b4a.from('v'))
|
|
const envG = { HOME: '/home/guest', PWD: '/home/guest', PATH: '/bin' }
|
|
const vfsG = createVfs(sys, personal, envG)
|
|
vfsG.bareOsIdentitySession = 'guest'
|
|
t.is(await vfsG.exists('/.bare/account'), false)
|
|
await t.exception(async () => {
|
|
await vfsG.readFile('/.bare/account')
|
|
})
|
|
const envU = {
|
|
HOME: '/home/abc123deadbe',
|
|
PWD: '/home/abc123deadbe',
|
|
PATH: '/bin',
|
|
UID: '999',
|
|
USER: 'abc123deadbe'
|
|
}
|
|
const vfsU = createVfs(sys, personal, envU)
|
|
vfsU.bareOsIdentitySession = 'unlocked'
|
|
t.is(b4a.toString(await vfsU.readFile('/.bare/account')), 'acctblob')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('BARE_OS_PERSONAL_ACCT_PREFIX nests guest tmp on personal drive', async (t) => {
|
|
const dir = testCorestoreDir('vfsacct')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvacct'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
BARE_OS_PERSONAL_ACCT_PREFIX: '1'
|
|
}
|
|
const vfs = createVfs(sys, personal, env)
|
|
await vfs.writeFile('/tmp/acct.txt', b4a.from('ok'))
|
|
t.is(
|
|
b4a.toString(await personal.get('/.bare-os/acct/_guest/tmp/guest/acct.txt')),
|
|
'ok'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /mnt lists HDMS mounts and allows writable put', async (t) => {
|
|
const dir = testCorestoreDir('vfsmnt')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvm'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = { HOME: '/home/u', PWD: '/home/u', PATH: '/bin' }
|
|
const extra = new Hyperdrive(store.namespace('exm', { writable: true }))
|
|
await extra.ready()
|
|
const mntRef = {
|
|
getMounts: () => new Map([['vault', { drive: extra, writable: true }]])
|
|
}
|
|
const vfs = createVfs(sys, personal, env, mntRef)
|
|
const root = await vfs.readdir('/')
|
|
t.ok(root.includes('mnt'))
|
|
t.alike(await vfs.readdir('/mnt'), ['vault'])
|
|
await vfs.writeFile('/mnt/vault/x.txt', b4a.from('ok'))
|
|
const buf = await extra.get('/x.txt')
|
|
t.ok(buf)
|
|
t.is(b4a.toString(buf), 'ok')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs /var in root readdir; /var/log empty lstat; writes map to personal', async (t) => {
|
|
const dir = testCorestoreDir('vfsvar')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvvar'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = { HOME: '/home/u', PWD: '/home/u', PATH: '/bin' }
|
|
const vfs = createVfs(sys, personal, env)
|
|
const root = await vfs.readdir('/')
|
|
t.ok(root.includes('var'))
|
|
t.alike(await vfs.readdir('/var'), ['log'])
|
|
const stLog = await vfs.lstat('/var/log')
|
|
t.ok(stLog)
|
|
t.is(stLog.type, 'directory')
|
|
await vfs.mkdir('/var/log/bare-os', { recursive: true })
|
|
await vfs.writeFile('/var/log/bare-os/test-vfs.log', b4a.from('hello'))
|
|
const buf = await personal.get('/.bare-os/var/log/u/bare-os/test-vfs.log')
|
|
t.ok(buf)
|
|
t.is(b4a.toString(buf), 'hello')
|
|
const sysTry = await sys.get('/.bare-os/var/log/u/bare-os/test-vfs.log')
|
|
t.is(sysTry, null)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs chdir /var/log from home', async (t) => {
|
|
const dir = testCorestoreDir('vfsvarcd')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvcv'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const vfs = createVfs(sys, personal, {
|
|
HOME: '/home/u',
|
|
PWD: '/home/u',
|
|
PATH: '/bin'
|
|
})
|
|
await vfs.chdir('/var/log')
|
|
t.is(vfs.getcwd(), '/var/log')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tokenize handles quotes and ops', async (t) => {
|
|
const tok = tokenize('ls -la | cat > out')
|
|
t.ok(tok.some((x) => x.type === 'op' && x.value === '|'))
|
|
t.ok(tok.some((x) => x.type === 'op' && x.value === '>'))
|
|
const w = tokenize("echo 'a b'")
|
|
const words = w.filter((x) => x.type === 'word').map((x) => x.value)
|
|
t.is(words.join(','), 'echo,a b')
|
|
})
|
|
|
|
test('tokenize records quote parts for glob (literal * in quotes)', async (t) => {
|
|
const u = tokenize('echo *')
|
|
const uwords = u.filter((x) => x.type === 'word')
|
|
const star = uwords[uwords.length - 1]
|
|
t.ok(star && star.type === 'word' && star.parts)
|
|
t.is(star.parts.length, 1)
|
|
t.is(star.parts[0].q, 'u')
|
|
t.is(star.parts[0].t, '*')
|
|
const sq = tokenize("echo '*'")
|
|
const sqw = sq.filter((x) => x.type === 'word')
|
|
t.is(sqw[0].value, 'echo')
|
|
const lit = sqw[1]
|
|
t.is(lit.parts.length, 1)
|
|
t.is(lit.parts[0].q, 's')
|
|
t.is(lit.parts[0].t, '*')
|
|
})
|
|
|
|
test('execShellLine pathname glob and noglob', async (t) => {
|
|
const dir = testCorestoreDir('shglob')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('sglob'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const echo = `
|
|
async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`
|
|
await drive.put('/bin/echo', b4a.from(echo))
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('/home/user/a.txt', b4a.from('x'))
|
|
await ctx.vfs.writeFile('/home/user/b.txt', b4a.from('y'))
|
|
const logs = []
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.chdir('/home/user')
|
|
await execShellLine(ctx, 'echo *.txt')
|
|
t.ok(
|
|
logs.some((l) => l.includes('a.txt') && l.includes('b.txt')),
|
|
logs.join('|')
|
|
)
|
|
logs.length = 0
|
|
await execShellLine(ctx, "echo '*.txt'")
|
|
t.ok(logs.some((l) => l === '*.txt'), logs.join('|'))
|
|
logs.length = 0
|
|
ctx.vfs.env.BARE_OS_SHELL_NOGLOB = '1'
|
|
await execShellLine(ctx, 'echo *.txt')
|
|
t.ok(logs.some((l) => l === '*.txt'))
|
|
delete ctx.vfs.env.BARE_OS_SHELL_NOGLOB
|
|
await execShellLine(ctx, 'set -f; echo *.txt')
|
|
t.ok(logs.some((l) => l === '*.txt'))
|
|
await execShellLine(ctx, 'set +f; echo *.txt')
|
|
t.ok(logs.some((l) => l.includes('a.txt')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine BARE_OS_SHELL_POSIX_MODE grouped list', async (t) => {
|
|
const dir = testCorestoreDir('shposixgroup')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('spg'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const echo = `
|
|
async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`
|
|
await drive.put('/bin/echo', b4a.from(echo))
|
|
const ctx = testCtx(drive, personal)
|
|
const logs = []
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.vfs.env.BARE_OS_SHELL_POSIX_MODE = '1'
|
|
await ctx.vfs.chdir('/home/user')
|
|
await execShellLine(ctx, '( echo hi )')
|
|
t.ok(logs.some((l) => l === 'hi'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('expandWord reads env', async (t) => {
|
|
t.is(expandWord('x${HOME}y', { HOME: '/h' }), 'x/hy')
|
|
})
|
|
|
|
test('expandWord $? and ${?} use BARE_OS_EXIT_STATUS', async (t) => {
|
|
const env = { [BARE_OS_EXIT_STATUS_ENV]: '7', HOME: '/h' }
|
|
t.is(expandWord('code=$?', env), 'code=7')
|
|
t.is(expandWord('c=${?}', env), 'c=7')
|
|
t.is(expandWord('missing=$?', {}), 'missing=0')
|
|
})
|
|
|
|
test('expandWord param expansion v2 (## %% :=)', async (t) => {
|
|
const base = {
|
|
BARE_OS_SHELL_PARAM_EXPANSION: '1',
|
|
BARE_OS_SHELL_PARAM_EXPANSION_V2: '1',
|
|
P: '/usr/bin/foo',
|
|
Q: 'caba'
|
|
}
|
|
t.is(expandWord('${P##*/}', base), 'foo')
|
|
t.is(expandWord('${P#*/}', base), 'usr/bin/foo')
|
|
t.is(expandWord('${Q%%ba}', base), 'ca')
|
|
t.is(expandWord('${Q%ba}', base), 'ca')
|
|
const assign = { ...base, EMPTY: '' }
|
|
t.is(expandWord('${EMPTY:=set}', assign), 'set')
|
|
t.is(assign.EMPTY, 'set')
|
|
})
|
|
|
|
/** POSIX-style parameter expansion vectors (Issue 7 subset; env-gated V3). */
|
|
test('expandWord param expansion v3 (:- :+ :?)', async (t) => {
|
|
const base = {
|
|
BARE_OS_SHELL_PARAM_EXPANSION: '1',
|
|
BARE_OS_SHELL_PARAM_EXPANSION_V3: '1',
|
|
HOME: '/home/u'
|
|
}
|
|
t.is(expandWord('${UNSET:-/default}', base), '/default')
|
|
t.is(expandWord('${EMPTY:-/alt}', { ...base, EMPTY: '' }), '/alt')
|
|
t.is(expandWord('${SET:-ignored}', { ...base, SET: 'ok' }), 'ok')
|
|
t.is(expandWord('${SET:+present}', { ...base, SET: 'v' }), 'present')
|
|
t.is(expandWord('${UNSET:+absent}', base), '')
|
|
t.exception(
|
|
() => expandWord('${UNSET:?hard fail}', base),
|
|
/hard fail|parameter null or unset/
|
|
)
|
|
t.exception(
|
|
() => expandWord('${EMPTY:?msg}', { ...base, EMPTY: '' }),
|
|
/msg|parameter null or unset/
|
|
)
|
|
})
|
|
|
|
test('syncBareOsExitStatusEnv and execShellLine update env', async (t) => {
|
|
const dir = testCorestoreDir('exstat')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pex'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/false',
|
|
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
|
|
)
|
|
await drive.put(
|
|
'/bin/true',
|
|
b4a.from(`async function run(ctx) { ctx.exitCode = 0 }`)
|
|
)
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
syncBareOsExitStatusEnv(ctx)
|
|
t.is(ctx.vfs.env[BARE_OS_EXIT_STATUS_ENV], '0')
|
|
await execShellLine(ctx, 'false')
|
|
t.is(ctx.vfs.env[BARE_OS_EXIT_STATUS_ENV], '1')
|
|
await execShellLine(ctx, 'true')
|
|
t.is(ctx.vfs.env[BARE_OS_EXIT_STATUS_ENV], '0')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine BARE_OS_SHELL_ERREXIT skips after failed command', async (t) => {
|
|
const dir = testCorestoreDir('errexit')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pex-e'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/false',
|
|
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
|
|
)
|
|
await drive.put(
|
|
'/bin/true',
|
|
b4a.from(`async function run(ctx) { ctx.exitCode = 0 }`)
|
|
)
|
|
await drive.put(
|
|
'/bin/echo',
|
|
b4a.from(
|
|
`async function run(ctx, argv) { ctx.console.log(argv.slice(1).join(' ')) }`
|
|
)
|
|
)
|
|
const logs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (m) => logs.push(String(m)),
|
|
error: (...a) => logs.push(a.map(String).join(' '))
|
|
}
|
|
ctx.vfs.env.BARE_OS_SHELL_ERREXIT = '1'
|
|
await execShellLine(ctx, 'false; echo AFTER')
|
|
t.ok(!logs.some((l) => l.includes('AFTER')))
|
|
logs.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'true; echo OK')
|
|
t.ok(logs.some((l) => l.includes('OK')))
|
|
logs.length = 0
|
|
ctx.exitCode = 0
|
|
delete ctx.vfs.env.BARE_OS_SHELL_ERREXIT
|
|
await execShellLine(ctx, 'set -e; false; echo NEVER')
|
|
t.ok(!logs.some((l) => l.includes('NEVER')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine errexit stops inside if-then body after failure', async (t) => {
|
|
const dir = testCorestoreDir('errexit-if')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pex-eif'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/false',
|
|
b4a.from(`async function run(ctx) { ctx.exitCode = 1 }`)
|
|
)
|
|
await drive.put(
|
|
'/bin/echo',
|
|
b4a.from(
|
|
`async function run(ctx, argv) { ctx.console.log(argv.slice(1).join(' ')) }`
|
|
)
|
|
)
|
|
const logs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (m) => logs.push(String(m)),
|
|
error: (...a) => logs.push(a.map(String).join(' '))
|
|
}
|
|
ctx.vfs.env.BARE_OS_SHELL_ERREXIT = '1'
|
|
await execShellLine(ctx, 'if true; then false; echo BAD; fi')
|
|
t.ok(!logs.some((l) => l.includes('BAD')))
|
|
logs.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'if false; then echo no; fi; echo AFTER')
|
|
t.ok(logs.some((l) => l.includes('AFTER')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('evaluateBareOsPeerAdmission honors dhtAddressClass with allowlist', async (t) => {
|
|
const env = {
|
|
BARE_OS_PEER_ALLOWLIST_HEX: '',
|
|
BARE_OS_DHT_ADDRESS_CLASS_ALLOWLIST: 'ipv4,relay'
|
|
}
|
|
const deny = evaluateBareOsPeerAdmission(env, 'aa', {
|
|
dhtAddressClass: 'ipv6'
|
|
})
|
|
t.is(deny.verdict, 'deny')
|
|
const ok = evaluateBareOsPeerAdmission(env, 'aa', { dhtAddressClass: 'ipv4' })
|
|
t.is(ok.verdict, 'allow')
|
|
})
|
|
|
|
test('BARE_OS_CTX_API_VERSION is semver-shaped', async (t) => {
|
|
t.ok(/^\d+\.\d+\.\d+$/.test(BARE_OS_CTX_API_VERSION))
|
|
})
|
|
|
|
test('getBareOsPipelineLimits reads BARE_OS_PIPELINE_* from env', async (t) => {
|
|
const def = getBareOsPipelineLimits({})
|
|
t.is(def.maxStages, DEFAULT_PIPELINE_MAX_STAGES)
|
|
const custom = getBareOsPipelineLimits({
|
|
BARE_OS_PIPELINE_MAX_STAGES: '4',
|
|
BARE_OS_PIPELINE_MAX_BYTES: '100',
|
|
BARE_OS_PIPELINE_MAX_LINES: '20'
|
|
})
|
|
t.is(custom.maxStages, 4)
|
|
t.is(custom.maxBytes, 100)
|
|
t.is(custom.maxLines, 20)
|
|
})
|
|
|
|
test('buildBareOsRuntimeCaps matches ctx API version and pipeline env', async (t) => {
|
|
const caps = buildBareOsRuntimeCaps({
|
|
BARE_OS_PIPELINE_MAX_STAGES: '8',
|
|
BARE_OS_CTX_API_VERSION
|
|
})
|
|
t.is(caps.ctxApiVersion, BARE_OS_CTX_API_VERSION)
|
|
t.is(caps.pipeline.maxStages, 8)
|
|
t.ok(Array.isArray(caps.pseudoFsPaths))
|
|
t.ok(caps.pseudoFsPaths.includes('/proc/version'))
|
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/boot_profile'))
|
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/ipc'))
|
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/session'))
|
|
t.ok(caps.pseudoFsPaths.includes('/proc/mounts'))
|
|
t.ok(caps.pseudoFsPaths.includes('/proc/bare_os_resources'))
|
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/virtual'))
|
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/unit-journal'))
|
|
t.ok(caps.pseudoFsPaths.includes('/snapshots'))
|
|
t.ok(caps.pseudoFsPaths.includes('/snapshots/system'))
|
|
t.is(caps.features.simulatedPipelines, true)
|
|
t.is(caps.features.httpDelegate, true)
|
|
t.is(caps.features.gitDelegate, true)
|
|
t.is(caps.features.systemctlDelegate, true)
|
|
t.is(caps.features.bareCtxModules, true)
|
|
t.is(caps.features.bareDriveBundles, true)
|
|
t.is(caps.features.bareHostImportsForCtx, true)
|
|
const capsOff = buildBareOsRuntimeCaps({ BARE_OS_BARE_MODULES: '0' })
|
|
t.is(capsOff.features.bareCtxModules, false)
|
|
t.is(capsOff.features.bareDriveBundles, false)
|
|
t.is(capsOff.features.bareHostImportsForCtx, false)
|
|
const capsIso = buildBareOsRuntimeCaps({ BARE_OS_BARE_HOST_IMPORTS: '0' })
|
|
t.is(capsIso.features.bareHostImportsForCtx, false)
|
|
t.is(capsIso.features.bareCtxModules, true)
|
|
})
|
|
|
|
test('bareOsBareModulesEnabled respects BARE_OS_BARE_MODULES', async (t) => {
|
|
t.ok(bareOsBareModulesEnabled({}))
|
|
t.ok(!bareOsBareModulesEnabled({ BARE_OS_BARE_MODULES: '0' }))
|
|
t.ok(!bareOsBareModulesEnabled({ BARE_OS_BARE_MODULES: 'false' }))
|
|
})
|
|
|
|
test('loadBareModuleManifest has entries', async (t) => {
|
|
const m = loadBareModuleManifest()
|
|
t.ok(m.version >= 1)
|
|
t.ok(Array.isArray(m.entries))
|
|
t.ok(m.entries.some((e) => e.ctxKey === 'b4a'))
|
|
})
|
|
|
|
function stableStringifyManifest(obj) {
|
|
if (obj === null || typeof obj !== 'object') return JSON.stringify(obj)
|
|
if (Array.isArray(obj)) {
|
|
return '[' + obj.map((x) => stableStringifyManifest(x)).join(',') + ']'
|
|
}
|
|
const keys = Object.keys(obj).sort()
|
|
return (
|
|
'{' +
|
|
keys
|
|
.map((k) => JSON.stringify(k) + ':' + stableStringifyManifest(obj[k]))
|
|
.join(',') +
|
|
'}'
|
|
)
|
|
}
|
|
|
|
test('bare-module-manifest.data.mjs matches bare-module-manifest.json', async (t) => {
|
|
const dir = path.dirname(fileURLToPath(import.meta.url))
|
|
const jsonPath = path.join(dir, 'lib', 'bare-module-manifest.json')
|
|
const fromJson = JSON.parse(await readFile(jsonPath, 'utf8'))
|
|
const embedded = bareOsBareModuleManifestEmbeddedRef()
|
|
t.is(stableStringifyManifest(fromJson), stableStringifyManifest(embedded))
|
|
})
|
|
|
|
test('buildBareCtxObjectFromHost loads core keys on Node', async (t) => {
|
|
const target = {}
|
|
await buildBareCtxObjectFromHost({}, target)
|
|
t.ok(target.b4a)
|
|
t.ok(target.compactEncoding)
|
|
t.ok(target.protomux)
|
|
})
|
|
|
|
test('maybeMergeBareFromDrive fills missing keys from bundle (mock vfs)', async (t) => {
|
|
const repoRoot = path.join(
|
|
fileURLToPath(new URL('.', import.meta.url)),
|
|
'..',
|
|
'..'
|
|
)
|
|
const bundleAbs = path.join(repoRoot, 'kernel/lib/bare/bundles/b4a.js')
|
|
const bundleSrc = await readFile(bundleAbs)
|
|
const manifest = {
|
|
version: 1,
|
|
bundles: [{ path: '/lib/bare/bundles/b4a.js', keys: ['b4a'] }]
|
|
}
|
|
const target = {}
|
|
const vfs = {
|
|
async readFile(p) {
|
|
if (p === '/lib/bare/manifest.json')
|
|
return b4a.from(JSON.stringify(manifest))
|
|
if (p === '/lib/bare/bundles/b4a.js') return new Uint8Array(bundleSrc)
|
|
return null
|
|
}
|
|
}
|
|
await maybeMergeBareFromDrive({}, vfs, target)
|
|
t.ok(target.b4a)
|
|
})
|
|
|
|
test('expandArgvAliases expands first word and keeps trailing argv', async (t) => {
|
|
t.alike(expandArgvAliases(['ll', 'z'], defaultShellAliases()), [
|
|
'ls',
|
|
'-la',
|
|
'z'
|
|
])
|
|
})
|
|
|
|
test('expandArgvAliases leaves sed unchanged', async (t) => {
|
|
t.alike(
|
|
expandArgvAliases(['sed', 's/a/b/', 'x.txt'], defaultShellAliases()),
|
|
['sed', 's/a/b/', 'x.txt']
|
|
)
|
|
})
|
|
|
|
test('defaultShellAliases does not remap sed', async (t) => {
|
|
t.is(defaultShellAliases().sed, undefined)
|
|
})
|
|
|
|
test('defaultShellAliases top maps to baretop', async (t) => {
|
|
t.is(defaultShellAliases().top, 'baretop')
|
|
})
|
|
|
|
test('defaultShellAliases btop maps to baretop', async (t) => {
|
|
t.is(defaultShellAliases().btop, 'baretop')
|
|
})
|
|
|
|
test('defaultShellAliases nano maps to edit', async (t) => {
|
|
t.is(defaultShellAliases().nano, 'edit')
|
|
t.alike(expandArgvAliases(['nano', 'x'], defaultShellAliases()), [
|
|
'edit',
|
|
'x'
|
|
])
|
|
})
|
|
|
|
test('expandArgvAliases throws on cyclic alias chain', async (t) => {
|
|
const cyclic = { a: 'b', b: 'a' }
|
|
t.exception(
|
|
() => expandArgvAliases(['a'], cyclic),
|
|
/alias: expansion nested too deeply/
|
|
)
|
|
})
|
|
|
|
test('loadBarerc applies export and alias from personal ~/.barerc', async (t) => {
|
|
const dir = testCorestoreDir('barerc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('brc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', '.barerc'),
|
|
b4a.from('export MYRC=1\nalias dog=echo woof\n')
|
|
)
|
|
const ctx = testCtx(drive, personal)
|
|
await loadBarerc(ctx)
|
|
t.is(ctx.vfs.env.MYRC, '1')
|
|
t.is(ctx.shellAliases.dog, 'echo woof')
|
|
t.is(ctx.shellAliases.ll, 'ls -la')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('loadBarerc createSkeletonIfMissing writes ~/.barerc when absent', async (t) => {
|
|
const dir = testCorestoreDir('barercskel')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('brcsk'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(drive, personal)
|
|
await loadBarerc(ctx, { createSkeletonIfMissing: true })
|
|
const back = await ctx.vfs.readFile('~/.barerc')
|
|
t.ok(back)
|
|
t.is(ctx.b4a.toString(back), BARERC_SKELETON)
|
|
t.is(ctx.shellAliases.ll, 'ls -la')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('loadBarerc theme nord sets REPL color env and LS_COLORS', async (t) => {
|
|
const dir = testCorestoreDir('barerctheme')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('brcth'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', '.barerc'),
|
|
b4a.from('theme nord\n')
|
|
)
|
|
const ctx = testCtx(drive, personal)
|
|
await loadBarerc(ctx)
|
|
t.is(ctx.vfs.env.BARE_OS_THEME, 'nord')
|
|
t.ok(
|
|
String(ctx.vfs.env.BARE_OS_COLOR_PROMPT || '').includes('38;2;'),
|
|
'nord preset uses truecolor prompt'
|
|
)
|
|
t.ok(
|
|
String(ctx.vfs.env.LS_COLORS || '').includes('di='),
|
|
'LS_COLORS set from preset'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine barerc reload reapplies barerc', async (t) => {
|
|
const dir = testCorestoreDir('barercrel')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('brcrel'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', '.barerc'),
|
|
b4a.from('export MARK=before\n')
|
|
)
|
|
const ctx = testCtx(drive, personal)
|
|
await loadBarerc(ctx)
|
|
t.is(ctx.vfs.env.MARK, 'before')
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', '.barerc'),
|
|
b4a.from('export MARK=after\n')
|
|
)
|
|
await execShellLine(ctx, 'barerc reload')
|
|
t.is(ctx.vfs.env.MARK, 'after')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('bareParseLsColors roundtrip', async (t) => {
|
|
const s = 'di=01;34:ln=36:ex=32:*.tar=01;31'
|
|
const m = bareParseLsColors(s)
|
|
t.is(m.di, '01;34')
|
|
t.is(m.ln, '36')
|
|
t.alike(bareParseLsColors(bareSerializeLsColors(m)), m)
|
|
})
|
|
|
|
test('bareLsColorOpenSgrFromMap directory and glob', async (t) => {
|
|
const map = bareParseLsColors('di=01;34:*.md=00;32')
|
|
const d = bareLsColorOpenSgrFromMap(
|
|
{ type: 'directory', mode: 0o040755 },
|
|
'foo',
|
|
map
|
|
)
|
|
t.ok(d.startsWith('\x1b['), 'directory colored')
|
|
const md = bareLsColorOpenSgrFromMap(
|
|
{ type: 'file', mode: 0o100644 },
|
|
'README.md',
|
|
map
|
|
)
|
|
t.ok(md.includes('32'), 'markdown glob')
|
|
})
|
|
|
|
test('bareLsColorOpenSgrFromMap mh for multi-link regular file', async (t) => {
|
|
const map = bareParseLsColors('mh=01;44:fi=40;31')
|
|
const one = bareLsColorOpenSgrFromMap(
|
|
{ type: 'file', mode: 0o100644, nlink: 1 },
|
|
'a',
|
|
map
|
|
)
|
|
const two = bareLsColorOpenSgrFromMap(
|
|
{ type: 'file', mode: 0o100644, nlink: 2 },
|
|
'a',
|
|
map
|
|
)
|
|
t.ok(two.includes('44'), 'nlink>1 uses mh SGR')
|
|
t.ok(one.includes('31'), 'nlink 1 uses fi')
|
|
})
|
|
|
|
test('bareLsColorOpenSgrFromMap ca when stat has capabilities', async (t) => {
|
|
const map = bareParseLsColors('ca=30;41:fi=00')
|
|
const sgr = bareLsColorOpenSgrFromMap(
|
|
{ type: 'file', mode: 0o100644, nlink: 1, capabilities: true },
|
|
'cap',
|
|
map
|
|
)
|
|
t.ok(sgr.includes('41'), 'capabilities use ca')
|
|
})
|
|
|
|
test('applyBareOsThemeFromEnv BARE_OS_COLOR_DEPTH=256 drops truecolor', async (t) => {
|
|
const ctx = {
|
|
vfs: { env: { BARE_OS_THEME: 'nord', BARE_OS_COLOR_DEPTH: '256' } }
|
|
}
|
|
await applyBareOsThemeFromEnv(ctx)
|
|
const p = String(ctx.vfs.env.BARE_OS_COLOR_PROMPT || '')
|
|
t.ok(p.includes('38;5;'), '256-color palette index SGR')
|
|
t.ok(!p.includes('38;2;'), 'no RGB truecolor')
|
|
})
|
|
|
|
test('dircolors -p includes TERM and di', async (t) => {
|
|
const db = bareDefaultDircolorsDatabase()
|
|
t.ok(db.includes('TERM'))
|
|
t.ok(db.includes('di '))
|
|
})
|
|
|
|
test('bareParseDircolorsDatabase TERM block', async (t) => {
|
|
const text = 'TERM xterm\ndi 01;34\nTERM none\nfi 00\nTERM *\nln 01;36\n'
|
|
const m = bareParseDircolorsDatabase(text, 'xterm')
|
|
t.is(m.di, '01;34')
|
|
t.is(m.ln, '01;36')
|
|
})
|
|
|
|
test('runBinCommand theme set writes barerc and updates env', async (t) => {
|
|
const themePath = path.join(__dirname, '../../kernel/bin/theme')
|
|
const themeSrc = await readFile(themePath, 'utf8')
|
|
const dir = testCorestoreDir('themecmd')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('thcmd'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/theme', b4a.from(themeSrc))
|
|
const ctx = testCtx(drive, personal)
|
|
await loadBarerc(ctx)
|
|
await runBinCommand(ctx, ['theme', 'set', 'dracula'])
|
|
t.is(ctx.vfs.env.BARE_OS_THEME, 'dracula')
|
|
const barc = ctx.b4a.toString(await ctx.vfs.readFile('~/.barerc'))
|
|
t.ok(barc.includes('theme dracula'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand dircolors -p prints database', async (t) => {
|
|
const dcPath = path.join(__dirname, '../../kernel/bin/dircolors')
|
|
const dcSrc = await readFile(dcPath, 'utf8')
|
|
const dir = testCorestoreDir('dircolp')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('dcp'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/dircolors', b4a.from(dcSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['dircolors', '-p'])
|
|
const out = lines.join('\n')
|
|
t.ok(out.includes('TERM'))
|
|
t.ok(out.includes('di'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand edit --help prints usage', async (t) => {
|
|
const editPath = path.join(__dirname, '../../kernel/bin/edit')
|
|
const editSrc = await readFile(editPath, 'utf8')
|
|
const dir = testCorestoreDir('edithlp')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('edh'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/edit', b4a.from(editSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['edit', '--help'])
|
|
const out = lines.join('\n')
|
|
t.ok(out.includes('usage:'))
|
|
t.ok(out.includes('TTY'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand edit requires TTY', async (t) => {
|
|
const editPath = path.join(__dirname, '../../kernel/bin/edit')
|
|
const editSrc = await readFile(editPath, 'utf8')
|
|
const dir = testCorestoreDir('ednotty')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('ednt'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/edit', b4a.from(editSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.replStdin = { isTTY: false }
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['edit', 'x.txt'])
|
|
t.is(ctx.exitCode, 1)
|
|
t.ok(lines.some((l) => l.includes('TTY')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand baretop --help prints usage', async (t) => {
|
|
const binPath = path.join(__dirname, '../../kernel/bin/baretop')
|
|
const src = await readFile(binPath, 'utf8')
|
|
const dir = testCorestoreDir('baretophlp')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('bth'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/baretop', b4a.from(src))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['baretop', '--help'])
|
|
const out = lines.join('\n')
|
|
t.ok(out.includes('usage:'))
|
|
t.ok(out.includes('TTY'))
|
|
t.ok(out.toLowerCase().includes('dashboard'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand baretop requires TTY', async (t) => {
|
|
const binPath = path.join(__dirname, '../../kernel/bin/baretop')
|
|
const src = await readFile(binPath, 'utf8')
|
|
const dir = testCorestoreDir('baretopntty')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('btnt'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/baretop', b4a.from(src))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.replStdin = { isTTY: false }
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['baretop'])
|
|
t.is(ctx.exitCode, 1)
|
|
t.ok(lines.some((l) => l.includes('TTY')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand nano --help matches edit bundle', async (t) => {
|
|
const nanoPath = path.join(__dirname, '../../kernel/bin/nano')
|
|
const nanoSrc = await readFile(nanoPath, 'utf8')
|
|
const dir = testCorestoreDir('nanohlp')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('nanh'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/nano', b4a.from(nanoSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['nano', '-h'])
|
|
const out = lines.join('\n')
|
|
t.ok(out.includes('usage:'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tokenize leaves echo 2 > file as stdout redirect not 2>', async (t) => {
|
|
const toks = tokenize('echo 2 > /tmp/x')
|
|
const words = toks.filter((x) => x.type === 'word').map((x) => x.value)
|
|
t.ok(words.includes('2'))
|
|
t.ok(toks.some((x) => x.type === 'op' && x.value === '>'))
|
|
t.ok(!toks.some((x) => x.type === 'op' && x.value === '2>'))
|
|
})
|
|
|
|
test('execShellLine stderr 2> and 2>&1 in pipeline', async (t) => {
|
|
const dir = testCorestoreDir('sherr')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('serr'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const emit = `
|
|
async function run(ctx) {
|
|
ctx.console.log('OUT')
|
|
ctx.console.error('ERR')
|
|
}
|
|
`
|
|
const cat = `
|
|
function bareStdin(ctx) {
|
|
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
|
|
}
|
|
async function run(ctx) {
|
|
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
|
|
}
|
|
`
|
|
await drive.put('/bin/emit', b4a.from(emit))
|
|
await drive.put('/bin/cat', b4a.from(cat))
|
|
const ctx = testCtx(drive, personal)
|
|
await execShellLine(ctx, 'emit 2>~/e.out')
|
|
const eb = await ctx.vfs.readFile('~/e.out')
|
|
t.ok(eb && ctx.b4a.toString(eb).includes('ERR'))
|
|
t.ok(!ctx.b4a.toString(eb).includes('OUT'))
|
|
await ctx.vfs.unlink('~/e.out')
|
|
const logs = []
|
|
ctx.console = {
|
|
log: (...a) => logs.push(['out', ...a]),
|
|
error: (...a) => logs.push(['err', ...a])
|
|
}
|
|
await execShellLine(ctx, 'emit 2>&1 | cat')
|
|
const piped = logs
|
|
.filter((x) => x[0] === 'out')
|
|
.map((x) => x.slice(1).join(' '))
|
|
t.ok(piped.some((l) => l.includes('OUT') && l.includes('ERR')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine pipeline < in and > out redirection', async (t) => {
|
|
const dir = testCorestoreDir('shpipeio')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('spio'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const cat = `
|
|
function bareStdin(ctx) {
|
|
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
|
|
}
|
|
async function run(ctx) {
|
|
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
|
|
}
|
|
`
|
|
await drive.put('/bin/cat', b4a.from(cat))
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('~/in.txt', b4a.from('pipeline-data\n'))
|
|
await execShellLine(ctx, 'cat < ~/in.txt | cat > ~/out.txt')
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(
|
|
b4a.toString(await ctx.vfs.readFile('~/out.txt'), 'utf8'),
|
|
'pipeline-data\n'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine pipeline first stage stdin < file only', async (t) => {
|
|
const dir = testCorestoreDir('shpipein')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('spin'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const cat = `
|
|
function bareStdin(ctx) {
|
|
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
|
|
}
|
|
async function run(ctx) {
|
|
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
|
|
}
|
|
`
|
|
const wcPath = path.join(__dirname, '../../kernel/bin/wc')
|
|
const wcSrc = await readFile(wcPath, 'utf8')
|
|
await drive.put('/bin/cat', b4a.from(cat))
|
|
await drive.put('/bin/wc', b4a.from(wcSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.writeFile('~/lines.txt', b4a.from('a\nb\nc\n'))
|
|
await execShellLine(ctx, 'wc -l < ~/lines.txt | cat')
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.some((l) => /^\s*3\s/.test(l) || l.includes('3')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine pipeline stage and byte limits', async (t) => {
|
|
const dir = testCorestoreDir('shpipe')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('spipe'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const echo = `
|
|
async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`
|
|
const cat = `
|
|
function bareStdin(ctx) {
|
|
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
|
|
}
|
|
async function run(ctx) {
|
|
ctx.console.log(bareStdin(ctx).replace(/\\n$/, ''))
|
|
}
|
|
`
|
|
await drive.put('/bin/echo', b4a.from(echo))
|
|
await drive.put('/bin/cat', b4a.from(cat))
|
|
const spam = `
|
|
async function run(ctx) {
|
|
ctx.console.log('z'.repeat(200))
|
|
}
|
|
`
|
|
await drive.put('/bin/spam', b4a.from(spam))
|
|
const logs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.env.BARE_OS_PIPELINE_MAX_STAGES = '2'
|
|
await execShellLine(ctx, 'echo a | echo b | echo c')
|
|
t.is(ctx.exitCode, 1)
|
|
t.ok(logs.some((l) => l.includes('BARE_OS_PIPELINE_MAX_STAGES')))
|
|
logs.length = 0
|
|
ctx.env.BARE_OS_PIPELINE_MAX_STAGES = '32'
|
|
ctx.env.BARE_OS_PIPELINE_MAX_BYTES = '80'
|
|
await execShellLine(ctx, 'spam | cat')
|
|
t.is(ctx.exitCode, 1)
|
|
t.ok(logs.some((l) => l.includes('BARE_OS_PIPELINE_MAX_BYTES')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('ls bareOsStdoutCaptured lists one name per line', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const dir = testCorestoreDir('lscap')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('plcap'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.writeFile('aaa', b4a.from(''))
|
|
await ctx.vfs.writeFile('lib', b4a.from(''))
|
|
await ctx.vfs.writeFile('zzz', b4a.from(''))
|
|
await runBinCommand(Object.assign({}, ctx, { bareOsStdoutCaptured: true }), [
|
|
'ls'
|
|
])
|
|
t.is(lines.length, 3)
|
|
t.ok(lines.includes('aaa') && lines.includes('lib') && lines.includes('zzz'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine ls pipe grep prints only matching entry line', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const grepPath = path.join(__dirname, '../../kernel/bin/grep')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const grepSrc = await readFile(grepPath, 'utf8')
|
|
const dir = testCorestoreDir('lspipegrep')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('plpg'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
await drive.put('/bin/grep', b4a.from(grepSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.writeFile('aaa', b4a.from(''))
|
|
await ctx.vfs.writeFile('lib', b4a.from(''))
|
|
await ctx.vfs.writeFile('zzz', b4a.from(''))
|
|
await execShellLine(ctx, 'ls | grep -F lib')
|
|
t.is(ctx.exitCode, 0)
|
|
t.alike(lines, ['lib'])
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine ls pipe wc -l counts lines', async (t) => {
|
|
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
|
|
const wcPath = path.join(__dirname, '../../kernel/bin/wc')
|
|
const lsSrc = await readFile(lsPath, 'utf8')
|
|
const wcSrc = await readFile(wcPath, 'utf8')
|
|
const dir = testCorestoreDir('lspipewc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('plwc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/ls', b4a.from(lsSrc))
|
|
await drive.put('/bin/wc', b4a.from(wcSrc))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await ctx.vfs.writeFile('a', b4a.from(''))
|
|
await ctx.vfs.writeFile('b', b4a.from(''))
|
|
await ctx.vfs.writeFile('c', b4a.from(''))
|
|
await execShellLine(ctx, 'ls | wc -l')
|
|
t.is(ctx.exitCode, 0)
|
|
const out = lines.join('\n').trim()
|
|
t.is(out, '3', 'three directory entries => three lines')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine runs cd and external', async (t) => {
|
|
const dir = testCorestoreDir('sh')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('psh'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/xy',
|
|
b4a.from(`
|
|
async function run(ctx, argv) {
|
|
ctx.got.push(argv.join(' '))
|
|
}
|
|
`)
|
|
)
|
|
const got = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.got = got
|
|
await execShellLine(ctx, 'cd /bin')
|
|
t.is(ctx.vfs.getcwd(), '/bin')
|
|
await execShellLine(ctx, 'xy one two')
|
|
t.is(got[0], 'xy one two')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine expands default ll to ls -la', async (t) => {
|
|
const dir = testCorestoreDir('llalias')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('lla'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/ls',
|
|
b4a.from(`
|
|
async function run(ctx, argv) {
|
|
ctx.got.push(argv.join(' '))
|
|
}
|
|
`)
|
|
)
|
|
const got = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.got = got
|
|
await execShellLine(ctx, 'll one')
|
|
t.is(got[0], 'ls -la one')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tokenize && || ; and split helpers', async (t) => {
|
|
const toks = tokenize('a&&b||c;d')
|
|
t.is(toks.map((x) => x.value).join(' '), 'a && b || c ; d')
|
|
const lists = splitTokensBySemicolon(toks)
|
|
t.is(lists.length, 2)
|
|
const { segments, ops } = splitTokensByAndOr(lists[0])
|
|
t.is(segments.length, 3)
|
|
t.is(ops.join(' '), '&& ||')
|
|
})
|
|
|
|
test('execShellLine && || ; short-circuit and exitCode', async (t) => {
|
|
const dir = testCorestoreDir('shandor')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('sand'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const stub = `
|
|
async function run(ctx, argv) {
|
|
const c = argv[0]
|
|
ctx.ran.push(c)
|
|
ctx.exitCode = c === 'false' ? 1 : 0
|
|
}
|
|
`
|
|
await drive.put('/bin/true', b4a.from(stub))
|
|
await drive.put('/bin/false', b4a.from(stub))
|
|
await drive.put(
|
|
'/bin/rec',
|
|
b4a.from(`
|
|
async function run(ctx, argv) {
|
|
ctx.ran.push('rec')
|
|
ctx.exitCode = 0
|
|
}
|
|
`)
|
|
)
|
|
const ran = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.ran = ran
|
|
ctx.exitCode = 0
|
|
|
|
await execShellLine(ctx, 'false && rec')
|
|
t.is(ran.join(','), 'false')
|
|
t.is(ctx.exitCode, 1)
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'false || rec')
|
|
t.is(ran.join(','), 'false,rec')
|
|
t.is(ctx.exitCode, 0)
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'true && rec')
|
|
t.is(ran.join(','), 'true,rec')
|
|
t.is(ctx.exitCode, 0)
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'true || rec')
|
|
t.is(ran.join(','), 'true')
|
|
t.is(ctx.exitCode, 0)
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'false || false || rec')
|
|
t.is(ran.join(','), 'false,false,rec')
|
|
t.is(ctx.exitCode, 0)
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'true && false || rec')
|
|
t.is(ran.join(','), 'true,false,rec')
|
|
t.is(ctx.exitCode, 0)
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'true; rec')
|
|
t.is(ran.join(','), 'true,rec')
|
|
|
|
await drive.put(
|
|
'/bin/last',
|
|
b4a.from(`
|
|
async function run(ctx, argv) {
|
|
ctx.ran.push('last')
|
|
ctx.exitCode = 0
|
|
}
|
|
`)
|
|
)
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'false | last')
|
|
t.is(ran.join(','), 'false,last', 'pipeline runs both stages')
|
|
t.is(ctx.exitCode, 0, 'POSIX-like: pipeline exit status is last stage')
|
|
|
|
ran.length = 0
|
|
ctx.exitCode = 0
|
|
await execShellLine(ctx, 'BARE_OS_SHELL_PIPEFAIL=1 false | last')
|
|
t.is(ran.join(','), 'false,last')
|
|
t.is(
|
|
ctx.exitCode,
|
|
1,
|
|
'pipefail: failure in an earlier stage overrides last stage success'
|
|
)
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine if then else fi', async (t) => {
|
|
const dir = testCorestoreDir('shif')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('sif'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/echo',
|
|
b4a.from(`
|
|
async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`)
|
|
)
|
|
await drive.put(
|
|
'/bin/true',
|
|
b4a.from(`
|
|
async function run(ctx) {
|
|
ctx.exitCode = 0
|
|
}
|
|
`)
|
|
)
|
|
await drive.put(
|
|
'/bin/false',
|
|
b4a.from(`
|
|
async function run(ctx) {
|
|
ctx.exitCode = 1
|
|
}
|
|
`)
|
|
)
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (s) => lines.push(String(s)),
|
|
error: (...a) => lines.push(a.join(' '))
|
|
}
|
|
await execShellLine(ctx, 'if false; then echo no; else echo yes; fi')
|
|
t.ok(lines.some((l) => l.includes('yes')))
|
|
lines.length = 0
|
|
await execShellLine(ctx, 'if true; then echo ok; fi')
|
|
t.ok(lines.some((l) => l.includes('ok')))
|
|
lines.length = 0
|
|
await execShellLine(ctx, 'if false; then echo x; fi')
|
|
t.ok(!lines.some((l) => l.includes('x')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('filenameFromContentDisposition parses attachment names', async (t) => {
|
|
t.is(
|
|
filenameFromContentDisposition('attachment; filename="a b.txt"'),
|
|
'a b.txt'
|
|
)
|
|
t.is(
|
|
filenameFromContentDisposition("attachment; filename*=UTF-8''x%20y.bin"),
|
|
'x y.bin'
|
|
)
|
|
t.is(filenameFromContentDisposition(null), null)
|
|
})
|
|
|
|
test('cron fieldMatches and dowFieldMatches', async (t) => {
|
|
t.ok(fieldMatches('*', 0, 0, 59))
|
|
t.ok(fieldMatches('*/5', 10, 0, 59))
|
|
t.ok(!fieldMatches('*/5', 11, 0, 59))
|
|
t.ok(fieldMatches('1-3', 2, 0, 59))
|
|
t.ok(!fieldMatches('1-3', 4, 0, 59))
|
|
t.ok(fieldMatches('1,4', 1, 0, 59))
|
|
t.ok(fieldMatches('1,4', 4, 0, 59))
|
|
t.ok(fieldMatches('1-10/2', 3, 0, 59))
|
|
t.ok(!fieldMatches('1-10/2', 4, 0, 59))
|
|
t.ok(dowFieldMatches('7', 0))
|
|
t.ok(!dowFieldMatches('7', 1))
|
|
t.ok(dowFieldMatches('0', 0))
|
|
t.ok(dowFieldMatches('1-5', 3))
|
|
})
|
|
|
|
test('cron parseCronLine and jobMatchesDate', async (t) => {
|
|
t.absent(parseCronLine(''))
|
|
t.absent(parseCronLine('# comment'))
|
|
t.absent(parseCronLine('0 0 * *'))
|
|
const j = parseCronLine('30 14 15 6 * echo hello world')
|
|
t.ok(j)
|
|
t.is(j.command, 'echo hello world')
|
|
const when = new Date(2020, 5, 15, 14, 30, 0)
|
|
t.ok(jobMatchesDate(j, when))
|
|
t.ok(!jobMatchesDate(j, new Date(2020, 5, 15, 14, 31, 0)))
|
|
const orJob = parseCronLine('0 12 15 * 1 echo dom-or-mon')
|
|
t.ok(orJob)
|
|
t.ok(
|
|
jobMatchesDate(orJob, new Date(2020, 5, 15, 12, 0, 0)),
|
|
'15th matches when dow also set (OR)'
|
|
)
|
|
t.ok(
|
|
jobMatchesDate(orJob, new Date(2020, 5, 8, 12, 0, 0)),
|
|
'Monday matches when dom also set (OR)'
|
|
)
|
|
t.ok(!jobMatchesDate(orJob, new Date(2020, 5, 16, 12, 0, 0)), 'Tue 16th no match')
|
|
const reboot = parseCronLine('@reboot echo hi')
|
|
t.ok(reboot)
|
|
t.is(reboot.minute, '@reboot')
|
|
t.is(reboot.command, 'echo hi')
|
|
t.ok(!jobMatchesDate(reboot, when))
|
|
const jit = parseCronLine('@reboot JitterSec=12 /bin/true')
|
|
t.ok(jit)
|
|
t.is(jit.jitterSec, 12)
|
|
t.is(jit.command, '/bin/true')
|
|
const jl = parseCronLine('* * * * * JitterSec=3 echo x')
|
|
t.ok(jl)
|
|
t.is(jl.jitterSec, 3)
|
|
t.is(jl.command, 'echo x')
|
|
})
|
|
|
|
test('systemctl list after startBareInitd shows kernel-logger and bare-cron', async (t) => {
|
|
const dir = testCorestoreDir('initctl')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pinit'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const logs = []
|
|
const ctx = testCtx(sys, personal)
|
|
ctx.execLine = async () => {}
|
|
await startBareInitd(ctx)
|
|
const dagRaw = getLastBareInitdDagSnapshotJson()
|
|
const dag = JSON.parse(String(dagRaw).trim())
|
|
t.ok(dag && dag.supervision && dag.supervision.schema === 1)
|
|
t.ok(Array.isArray(dag.supervision.restartKeys))
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['systemctl', 'list'])
|
|
t.is(ctx.exitCode, 0)
|
|
const text = logs.join('\n')
|
|
t.ok(text.includes('kernel-logger'))
|
|
t.ok(text.includes('bare-cron'))
|
|
stopBareInitd()
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('systemctl list-units delegates to same backend', async (t) => {
|
|
const dir = testCorestoreDir('initctlsys')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pics'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const logs = []
|
|
const ctx = testCtx(sys, personal)
|
|
ctx.execLine = async () => {}
|
|
await startBareInitd(ctx)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['systemctl', 'list-units'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(logs.join('\n').includes('bare-cron'))
|
|
stopBareInitd()
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('systemctl is-active matches bare-initd runtime', async (t) => {
|
|
const dir = testCorestoreDir('initctlactive')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pica'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const logs = []
|
|
const ctx = testCtx(sys, personal)
|
|
ctx.execLine = async () => {}
|
|
await startBareInitd(ctx)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['systemctl', 'is-active', 'kernel-logger'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(logs.join('\n').includes('active'))
|
|
logs.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['systemctl', 'is-active', 'nonexistent-unit'])
|
|
t.is(ctx.exitCode, 3)
|
|
stopBareInitd()
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('systemctl restart bare-cron succeeds', async (t) => {
|
|
const dir = testCorestoreDir('initctlrst')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('picr'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const logs = []
|
|
const ctx = testCtx(sys, personal)
|
|
ctx.execLine = async () => {}
|
|
await startBareInitd(ctx)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['systemctl', 'restart', 'bare-cron'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(logs.some((l) => /restarted bare-cron/i.test(l)))
|
|
stopBareInitd()
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('initd disabled.txt skips unit on boot; systemctl enable undoes', async (t) => {
|
|
const dir = testCorestoreDir('initdis')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pdis'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(sys, personal)
|
|
ctx.execLine = async () => {}
|
|
await ctx.vfs.mkdir('/home/user/.config/bare-os/initd', { recursive: true })
|
|
await ctx.vfs.writeFile(
|
|
'/home/user/.config/bare-os/initd/disabled.txt',
|
|
b4a.from('bare-cron\n')
|
|
)
|
|
await startBareInitd(ctx)
|
|
t.is(getBareServiceRuntime('bare-cron'), undefined)
|
|
t.is(getBareServiceRuntime('kernel-logger')?.phase, 'active')
|
|
stopBareInitd()
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log: () => {},
|
|
error: () => {}
|
|
}
|
|
await runBinCommand(ctx, ['systemctl', 'enable', 'bare-cron'])
|
|
t.is(ctx.exitCode, 0)
|
|
await startBareInitd(ctx)
|
|
t.is(getBareServiceRuntime('bare-cron')?.phase, 'active')
|
|
stopBareInitd()
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('stopBareInitd runs registered disposers', async (t) => {
|
|
let n = 0
|
|
registerBareInitdDisposer(() => {
|
|
n++
|
|
})
|
|
stopBareInitd()
|
|
t.is(n, 1)
|
|
stopBareInitd()
|
|
t.is(n, 2)
|
|
})
|
|
|
|
test('runKernelShutdownHooks runs LIFO once', async (t) => {
|
|
const o = []
|
|
registerKernelShutdownHook(async () => {
|
|
o.push('a')
|
|
})
|
|
registerKernelShutdownHook(async () => {
|
|
o.push('b')
|
|
})
|
|
await runKernelShutdownHooks()
|
|
t.is(o.join(','), 'b,a')
|
|
await runKernelShutdownHooks()
|
|
t.is(o.join(','), 'b,a')
|
|
})
|
|
|
|
test('bareInitdShutdownActiveUnitsReverse is safe when nothing is active', async (t) => {
|
|
stopBareInitd()
|
|
await bareInitdShutdownActiveUnitsReverse({ vfs: null, console, env: {} })
|
|
t.pass()
|
|
})
|
|
|
|
test('/proc/bare_os/clock.json is readable pseudo JSON', async (t) => {
|
|
const dir = testCorestoreDir('clock-proc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const bootStartedMs = Date.now() - 50
|
|
const shellEnv = {
|
|
HOME: '/home/user',
|
|
PATH: '/bin',
|
|
USER: 'user',
|
|
UID: '1000',
|
|
GID: '1000',
|
|
PWD: '/home/user',
|
|
BARE_OS_EXIT_STATUS: '0'
|
|
}
|
|
const vfs = createVfs(drive, personal, shellEnv, null, {
|
|
bareOsIpc: createBareOsIpc(),
|
|
procBareOsClockText() {
|
|
const wallMs = Date.now()
|
|
return `${JSON.stringify({
|
|
schema: 1,
|
|
CLOCK_REALTIME_MS: wallMs,
|
|
CLOCK_BOOTTIME_RELATIVE_MS: wallMs - bootStartedMs,
|
|
CLOCK_MONOTONIC_NS_FROM_PERF: null
|
|
})}\n`
|
|
}
|
|
})
|
|
const raw = b4a.toString(await vfs.readFile('/proc/bare_os/clock.json'))
|
|
const j = JSON.parse(raw)
|
|
t.is(j.schema, 1)
|
|
t.ok(typeof j.CLOCK_REALTIME_MS === 'number')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('/proc/self/fd lists extras from getProcSelfExtraFds', async (t) => {
|
|
const dir = testCorestoreDir('self-fd-extra')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const shellEnv = {
|
|
HOME: '/home/user',
|
|
PATH: '/bin',
|
|
USER: 'user',
|
|
UID: '1000',
|
|
GID: '1000',
|
|
PWD: '/home/user',
|
|
BARE_OS_EXIT_STATUS: '0'
|
|
}
|
|
const vfs = createVfs(drive, personal, shellEnv, null, {
|
|
bareOsIpc: createBareOsIpc(),
|
|
getProcSelfExtraFds: () => [{ fdNum: '7', target: 'pipe:[bare-test]' }]
|
|
})
|
|
const names = await vfs.readdir('/proc/self/fd')
|
|
t.ok(names.includes('7'))
|
|
const txt = b4a.toString(await vfs.readFile('/proc/self/fd/7'))
|
|
t.ok(txt.includes('pipe:[bare-test]'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('fish-readline stripAnsi and fuzzyMatch', async (t) => {
|
|
t.is(stripAnsi('\x1b[32mhi\x1b[0m'), 'hi')
|
|
t.ok(fuzzyMatch('hello', 'hlo'))
|
|
t.ok(!fuzzyMatch('hello', 'hxo'))
|
|
})
|
|
|
|
test('fish-readline history parse format dedupe search', async (t) => {
|
|
const parsed = parseHistoryFile('#1:a\n#2:b\n')
|
|
t.is(parsed.length, 2)
|
|
t.is(parsed[0].command, 'a')
|
|
const round = parseHistoryFile(formatHistoryFile(parsed))
|
|
t.is(round[1].command, 'b')
|
|
const deduped = dedupeConsecutiveHistory([
|
|
{ timestamp: 1, command: 'x' },
|
|
{ timestamp: 2, command: 'x' },
|
|
{ timestamp: 3, command: 'y' }
|
|
])
|
|
t.is(deduped.length, 2)
|
|
t.is(deduped[1].command, 'y')
|
|
const hist = [{ command: 'aa' }, { command: 'ba' }]
|
|
const found = searchHistoryEntries(hist, 'a')
|
|
t.ok(found.includes('ba'))
|
|
t.ok(found.includes('aa'))
|
|
})
|
|
|
|
test('tier-1 cat from system drive', async (t) => {
|
|
const dir = testCorestoreDir('cat')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/cat', b4a.from(await readBuiltBin('cat')))
|
|
await drive.put('/etc/x', b4a.from('hello'))
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log() {},
|
|
error() {}
|
|
}
|
|
const catOut = []
|
|
const prevCatWrite = process.stdout.write
|
|
process.stdout.write = (chunk) => {
|
|
catOut.push(
|
|
typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8')
|
|
)
|
|
return true
|
|
}
|
|
try {
|
|
await runBinCommand(ctx, ['cat', '/etc/x'])
|
|
} finally {
|
|
process.stdout.write = prevCatWrite
|
|
}
|
|
t.is(catOut.join(''), 'hello')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 tail head options and tail -f poll', async (t) => {
|
|
const dir = testCorestoreDir('tail-head')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pth'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/tail', b4a.from(await readBuiltBin('tail')))
|
|
await drive.put('/bin/head', b4a.from(await readBuiltBin('head')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal, {
|
|
BARE_OS_TAIL_F_POLL_MS: '25',
|
|
BARE_OS_TAIL_F_MAX_ROUNDS: '8'
|
|
})
|
|
ctx.exitCode = 0
|
|
ctx.bareOsRuntimeCaps = buildBareOsRuntimeCaps(ctx.env)
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('t.txt', b4a.from('one\ntwo\nthree\n'))
|
|
await runBinCommand(ctx, ['tail', '-n', '2', 't.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'two\nthree')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['tail', '-n', '+2', 't.txt'])
|
|
t.is(lines.join('\n'), 'two\nthree')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['head', '-c', '4', 't.txt'])
|
|
t.is(lines[0], 'one\n')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await ctx.vfs.writeFile('f.log', b4a.from('a\n'))
|
|
const tailP = runBinCommand(ctx, ['tail', '-f', 'f.log'])
|
|
await new Promise((r) => setTimeout(r, 15))
|
|
const cur = b4a.toString(await ctx.vfs.readFile('f.log'))
|
|
await ctx.vfs.writeFile('f.log', b4a.from(cur + 'b\n'))
|
|
await tailP
|
|
t.ok(lines.includes('a'))
|
|
t.ok(lines.includes('b'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = 'x\ny\n'
|
|
await runBinCommand(ctx, ['tail', '-f'])
|
|
t.is(ctx.exitCode, 1)
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 grep and mkfifo from system drive', async (t) => {
|
|
const dir = testCorestoreDir('grep')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pg'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/grep', b4a.from(await readBuiltBin('grep')))
|
|
await drive.put('/bin/mkfifo', b4a.from(await readBuiltBin('mkfifo')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile(
|
|
'w.txt',
|
|
b4a.from('aaa\nneedle line\nbbb\nneedle line two')
|
|
)
|
|
await runBinCommand(ctx, ['grep', '-F', 'needle', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.length, 2)
|
|
t.ok(lines[0].includes('needle line'))
|
|
t.ok(lines[1].includes('needle line two'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-F', 'nomatch', 'w.txt'])
|
|
t.is(ctx.exitCode, 1)
|
|
t.is(lines.length, 0)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-v', '-F', 'needle', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'aaa\nbbb')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-c', '-F', 'needle', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], '2')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-n', '-F', 'bbb', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], '3:bbb')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = 'alpha\nbeta\n'
|
|
await runBinCommand(ctx, ['grep', '-F', 'beta'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], 'beta')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-x', '-F', 'aaa', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], 'aaa')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-m', '1', '-F', 'needle', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.length, 1)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-o', 'ee', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.includes('ee'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['mkfifo', '/run/bare-os/ipc/t1'])
|
|
t.is(ctx.exitCode, 0)
|
|
const readP = ctx.vfs.readFile('/run/bare-os/ipc/t1')
|
|
await ctx.vfs.writeFile('/run/bare-os/ipc/t1', b4a.from('ok'))
|
|
t.is(b4a.toString(await readP), 'ok')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['grep', '-F', '-C', '1', 'needle', 'w.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.some((l) => l === 'aaa'))
|
|
t.ok(lines.some((l) => l.includes('needle line')))
|
|
t.ok(lines.some((l) => l === 'bbb'))
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 sort and wc flags', async (t) => {
|
|
const dir = testCorestoreDir('sort-wc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('psw'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/sort', b4a.from(await readBuiltBin('sort')))
|
|
await drive.put('/bin/wc', b4a.from(await readBuiltBin('wc')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('nums.txt', b4a.from('10\n2\n1\n'))
|
|
await runBinCommand(ctx, ['sort', '-n', 'nums.txt'])
|
|
t.is(lines.join('\n'), '1\n2\n10')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sort', '-nru', 'nums.txt'])
|
|
t.is(lines.join('\n'), '10\n2\n1')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['wc', '-l', 'nums.txt'])
|
|
t.is(lines[0].trim(), '3 nums.txt')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['wc', '-c', 'nums.txt'])
|
|
t.is(lines[0], ' 7 nums.txt')
|
|
|
|
const errs = []
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(...a) {
|
|
errs.push(a.join(' '))
|
|
}
|
|
}
|
|
await ctx.vfs.writeFile('ord.txt', b4a.from('a\nb\nc\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sort', '-c', 'ord.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(errs.length, 0)
|
|
|
|
await ctx.vfs.writeFile('bad.txt', b4a.from('b\na\n'))
|
|
lines.length = 0
|
|
errs.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sort', '-c', 'bad.txt'])
|
|
t.is(ctx.exitCode, 1)
|
|
t.ok(errs.some((e) => e.includes('disorder')))
|
|
|
|
errs.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sort', '-C', 'bad.txt'])
|
|
t.is(ctx.exitCode, 1)
|
|
t.is(errs.length, 0)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sort', '-o', 'sorted.txt', '-n', 'nums.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
const sortedBuf = await ctx.vfs.readFile('sorted.txt')
|
|
t.is(ctx.b4a.toString(sortedBuf), '1\n2\n10\n')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sort', '-s', 'ord.txt'])
|
|
t.is(lines.join('\n'), 'a\nb\nc')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 find du basename options', async (t) => {
|
|
const dir = testCorestoreDir('fdu-base')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pfdb'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/find', b4a.from(await readBuiltBin('find')))
|
|
await drive.put('/bin/du', b4a.from(await readBuiltBin('du')))
|
|
await drive.put('/bin/basename', b4a.from(await readBuiltBin('basename')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('Lo.txt', b4a.from('ab'))
|
|
await runBinCommand(ctx, ['find', '.', '-iname', 'lo.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.some((p) => /Lo\.txt$/.test(String(p).replace(/\\/g, '/'))))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['du', '-h', 'Lo.txt'])
|
|
t.ok(/^2\tLo\.txt$/.test(lines[0]) || /^3\tLo\.txt$/.test(lines[0]))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['basename', '-a', '/x/a', '/y/b'])
|
|
t.is(lines.join('\n'), 'a\nb')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['basename', 'z.h', '.h'])
|
|
t.is(lines[0], 'z')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('execShellLine find pipes to wc -l', async (t) => {
|
|
const dir = testCorestoreDir('findpipewc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('fpwc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/find', b4a.from(await readBuiltBin('find')))
|
|
await drive.put('/bin/wc', b4a.from(await readBuiltBin('wc')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('p.txt', b4a.from(''))
|
|
await execShellLine(ctx, 'find . -maxdepth 1 | wc -l')
|
|
t.is(ctx.exitCode, 0)
|
|
const n = Number.parseInt(String(lines[0] || '').trim(), 10)
|
|
t.ok(Number.isFinite(n) && n >= 1)
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 date format and test numeric', async (t) => {
|
|
const dir = testCorestoreDir('date-test')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pdt'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/date', b4a.from(await readBuiltBin('date')))
|
|
await drive.put('/bin/test', b4a.from(await readBuiltBin('test')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await runBinCommand(ctx, ['test', '2', '-lt', '5'])
|
|
t.is(ctx.exitCode, 0)
|
|
await runBinCommand(ctx, ['test', '5', '-lt', '2'])
|
|
t.is(ctx.exitCode, 1)
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['date', '+%Y'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(/^\d{4}$/.test(lines[0]))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 find -mindepth skips shallow paths', async (t) => {
|
|
const dir = testCorestoreDir('find-mindepth')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pfindmd'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/find', b4a.from(await readBuiltBin('find')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.mkdir('nest', { recursive: true })
|
|
await ctx.vfs.mkdir('nest/sub', { recursive: true })
|
|
await ctx.vfs.writeFile('nest/a.txt', b4a.from('a'))
|
|
await ctx.vfs.writeFile('nest/sub/b.txt', b4a.from('b'))
|
|
await runBinCommand(ctx, ['find', '.', '-mindepth', '2', '-type', 'f'])
|
|
t.is(ctx.exitCode, 0)
|
|
const norm = lines.map((p) => p.replace(/\\/g, '/')).sort()
|
|
t.ok(norm.some((p) => p.endsWith('/nest/a.txt')))
|
|
t.ok(norm.some((p) => p.endsWith('/nest/sub/b.txt')))
|
|
lines.length = 0
|
|
await runBinCommand(ctx, ['find', 'nest', '-mindepth', '2', '-type', 'f'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(
|
|
lines.some((p) => String(p).replace(/\\/g, '/').endsWith('/nest/sub/b.txt'))
|
|
)
|
|
t.ok(
|
|
!lines.some((p) => String(p).replace(/\\/g, '/').endsWith('/nest/a.txt'))
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs union write deny rejects chmod and symlink targets', async (t) => {
|
|
const dir = testCorestoreDir('union-deny')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pu'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const shellEnv = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
USER: 'guest',
|
|
UID: '0',
|
|
GID: '0'
|
|
}
|
|
const vfs = createVfs(sys, personal, shellEnv, null, {
|
|
bareOsIpc: createBareOsIpc(),
|
|
unionReadPrefixes: ['/etc'],
|
|
unionWriteDenyPrefixes: ['/etc']
|
|
})
|
|
let w = false
|
|
try {
|
|
await vfs.writeFile('/etc/union-blocked', b4a.from('x'))
|
|
} catch (e) {
|
|
w = true
|
|
t.ok(String(e.message).includes('union write denied'))
|
|
}
|
|
t.ok(w)
|
|
let c = false
|
|
try {
|
|
await vfs.chmod('/etc/passwd', 0o644)
|
|
} catch (e) {
|
|
c = true
|
|
t.ok(String(e.message).includes('union write denied'))
|
|
}
|
|
t.ok(c)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs symlink relative and absolute targets under personal', async (t) => {
|
|
const dir = testCorestoreDir('symlink-personal')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('slp'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('~/target.txt', b4a.from('hello'))
|
|
await ctx.vfs.symlink('target.txt', '~/via_rel.txt')
|
|
t.is(await ctx.vfs.readlink('~/via_rel.txt'), 'target.txt')
|
|
const st = await ctx.vfs.lstat('~/via_rel.txt')
|
|
t.is(st.type, 'symlink')
|
|
t.is(b4a.toString(await ctx.vfs.readFile('~/via_rel.txt'), 'utf8'), 'hello')
|
|
|
|
await ctx.vfs.symlink('/home/user/target.txt', '~/via_abs.txt')
|
|
t.is(await ctx.vfs.readlink('~/via_abs.txt'), '/home/user/target.txt')
|
|
t.is(b4a.toString(await ctx.vfs.readFile('~/via_abs.txt'), 'utf8'), 'hello')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('vfs symlink on system drive /var follows within same drive', async (t) => {
|
|
const dir = testCorestoreDir('symlink-system-var')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('slsv'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const dataPath = '/var/bare-os-symlink-test/data.txt'
|
|
const aliasPath = '/var/bare-os-symlink-test/alias.txt'
|
|
await drive.put(dataPath, b4a.from('sysblob'))
|
|
await drive.symlink(aliasPath, dataPath, { metadata: {} })
|
|
const ctx = testCtx(drive, personal)
|
|
t.is(await ctx.vfs.readlink(aliasPath), dataPath)
|
|
t.is(b4a.toString(await ctx.vfs.readFile(aliasPath), 'utf8'), 'sysblob')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('HyperDHT local testnet: bootstrap node and ephemeral peer fullyBootstrapped', async (t) => {
|
|
const createTestnet = require('hyperdht/testnet.js')
|
|
const DHT = require('hyperdht')
|
|
const testnet = await createTestnet(1)
|
|
try {
|
|
t.ok(testnet.bootstrap.length >= 1, 'testnet exposes bootstrap address')
|
|
const addr = testnet.nodes[0].address()
|
|
t.ok(addr && addr.port > 0, 'bootstrap listens on a UDP port')
|
|
const client = new DHT({
|
|
ephemeral: true,
|
|
bootstrap: testnet.bootstrap,
|
|
host: '127.0.0.1'
|
|
})
|
|
try {
|
|
await client.fullyBootstrapped()
|
|
t.pass('ephemeral HyperDHT node reaches fullyBootstrapped against local testnet')
|
|
} finally {
|
|
await client.destroy()
|
|
}
|
|
} finally {
|
|
await testnet.destroy()
|
|
}
|
|
})
|
|
|
|
test('tier-1 mv renames file in home', async (t) => {
|
|
const dir = testCorestoreDir('mv-home')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('mvhm'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/mv', b4a.from(await readBuiltBin('mv')))
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('a.txt', b4a.from('ok'))
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['mv', 'a.txt', 'b.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(b4a.toString(await ctx.vfs.readFile('b.txt'), 'utf8'), 'ok')
|
|
t.ok(
|
|
(await ctx.vfs.readFile('a.txt')) == null,
|
|
'mv removed source path in home'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 cmp silent and kernel-boot-diff line set', async (t) => {
|
|
const dir = testCorestoreDir('cmp-kbd')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('cmpkbd'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const cmpSrc = await readFile(
|
|
path.join(__dirname, '../../kernel/bin/cmp'),
|
|
'utf8'
|
|
)
|
|
const kbdSrc = await readFile(
|
|
path.join(__dirname, '../../kernel/bin/kernel-boot-diff'),
|
|
'utf8'
|
|
)
|
|
await drive.put('/bin/cmp', b4a.from(cmpSrc))
|
|
await drive.put('/bin/kernel-boot-diff', b4a.from(kbdSrc))
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('same1.txt', b4a.from('x'))
|
|
await ctx.vfs.writeFile('same2.txt', b4a.from('x'))
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['cmp', '-s', 'same1.txt', 'same2.txt'])
|
|
t.is(ctx.exitCode, 0, 'cmp -s identical')
|
|
await ctx.vfs.writeFile('d2.txt', b4a.from('y'))
|
|
ctx.exitCode = 0
|
|
const errs = []
|
|
ctx.console = {
|
|
log() {},
|
|
error(s) {
|
|
errs.push(String(s))
|
|
}
|
|
}
|
|
await runBinCommand(ctx, ['cmp', '-s', 'same1.txt', 'd2.txt'])
|
|
t.is(ctx.exitCode, 1, 'cmp -s differs')
|
|
await ctx.vfs.writeFile('boot-a.txt', b4a.from('line1\nline2\n'))
|
|
await ctx.vfs.writeFile('boot-b.txt', b4a.from('line1\nline3\n'))
|
|
const lines = []
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(s) {
|
|
errs.push(String(s))
|
|
}
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['kernel-boot-diff', 'boot-a.txt', 'boot-b.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.some((l) => l.startsWith('+ ') && l.includes('line3')))
|
|
t.ok(lines.some((l) => l.startsWith('- ') && l.includes('line2')))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 diff -u and patch apply stdin hunk', async (t) => {
|
|
const dir = testCorestoreDir('diffpatch')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('dp'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/diff', b4a.from(await readBuiltBin('diff')))
|
|
await drive.put('/bin/patch', b4a.from(await readBuiltBin('patch')))
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('a.txt', b4a.from('alpha\n'))
|
|
await ctx.vfs.writeFile('b.txt', b4a.from('beta\n'))
|
|
const out = []
|
|
ctx.console = {
|
|
log(s) {
|
|
out.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['diff', '-u', 'a.txt', 'b.txt'])
|
|
t.is(ctx.exitCode, 1)
|
|
t.ok(out.join('\n').includes('+++ b.txt'))
|
|
await ctx.vfs.writeFile('fix.txt', b4a.from('alpha\n'))
|
|
ctx.shellStdin = out.join('\n') + '\n'
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['patch', '-p0', 'fix.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(ctx.b4a.toString(await ctx.vfs.readFile('fix.txt')), 'beta\n')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('bareInitdReadinessSnapshot exposes unit table', async (t) => {
|
|
const s = bareInitdReadinessSnapshot()
|
|
t.is(s.schema, 2)
|
|
t.ok(Array.isArray(s.units))
|
|
t.ok(s.supervisionTelemetry && s.supervisionTelemetry.schema === 1)
|
|
})
|
|
|
|
test('tier-1 test -r -w -x -s predicates', async (t) => {
|
|
const dir = testCorestoreDir('test-pred')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('tpred'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/test', b4a.from(await readBuiltBin('test')))
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('ro.txt', b4a.from('data'))
|
|
await ctx.vfs.chmod('ro.txt', 0o400)
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['test', '-r', 'ro.txt'])
|
|
t.is(ctx.exitCode, 0, '-r read-only file')
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['test', '-w', 'ro.txt'])
|
|
t.is(ctx.exitCode, 1, '-w false when not writable')
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['test', '-s', 'ro.txt'])
|
|
t.is(ctx.exitCode, 0, '-s nonempty file')
|
|
await ctx.vfs.writeFile('empty.txt', b4a.from(''))
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['test', '-s', 'empty.txt'])
|
|
t.is(ctx.exitCode, 1, '-s empty file')
|
|
await ctx.vfs.writeFile('run.sh', b4a.from('#!'))
|
|
await ctx.vfs.chmod('run.sh', 0o700)
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['test', '-x', 'run.sh'])
|
|
t.is(ctx.exitCode, 0, '-x executable')
|
|
await ctx.vfs.chmod('run.sh', 0o600)
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['test', '-x', 'run.sh'])
|
|
t.is(ctx.exitCode, 1, '-x false without exec bit')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 find -perm filters by mode', async (t) => {
|
|
const dir = testCorestoreDir('find-perm')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pfp'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/find', b4a.from(await readBuiltBin('find')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('rw.txt', b4a.from(''))
|
|
await ctx.vfs.chmod('rw.txt', 0o644)
|
|
await ctx.vfs.writeFile('x.txt', b4a.from(''))
|
|
await ctx.vfs.chmod('x.txt', 0o600)
|
|
await runBinCommand(ctx, ['find', '.', '-perm', '600', '-type', 'f'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(
|
|
lines.some((p) => String(p).replace(/\\/g, '/').endsWith('/x.txt'))
|
|
)
|
|
t.ok(
|
|
!lines.some((p) => String(p).replace(/\\/g, '/').endsWith('/rw.txt'))
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 find -mtime -1 matches recent files', async (t) => {
|
|
const dir = testCorestoreDir('find-mtime')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('fmt'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/find', b4a.from(await readBuiltBin('find')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('recent.txt', b4a.from('x'))
|
|
await runBinCommand(ctx, ['find', '.', '-type', 'f', '-mtime', '-1'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(
|
|
lines.some((p) => String(p).replace(/\\/g, '/').endsWith('/recent.txt'))
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 find -xdev skips other volume under /', async (t) => {
|
|
const dir = testCorestoreDir('find-xdev')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('fxd'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/onlyroot.txt', b4a.from('sys'))
|
|
await drive.put('/bin/find', b4a.from(await readBuiltBin('find')))
|
|
const ctx = testCtx(drive, personal, { PWD: '/', HOME: '/home/user' })
|
|
ctx.exitCode = 0
|
|
const lines = []
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('/home/user/only_in_home.txt', b4a.from('p'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['find', '/', '-xdev', '-name', 'only_in_home.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.length, 0, '-xdev from / must not descend into /home')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['find', '/', '-name', 'only_in_home.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(
|
|
lines.some((p) =>
|
|
String(p).replace(/\\/g, '/').endsWith('/only_in_home.txt')
|
|
),
|
|
'without -xdev, file under /home is visible from /'
|
|
)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['find', '/', '-xdev', '-name', 'onlyroot.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(
|
|
lines.some((p) =>
|
|
String(p).replace(/\\/g, '/').endsWith('/onlyroot.txt')
|
|
),
|
|
'-xdev still searches system tree under /'
|
|
)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tar-cli hard link extract copies unless BARE_OS_VFS_STRICT_HARDLINK', async (t) => {
|
|
const dir = testCorestoreDir('tar-hardlink')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('tarhl'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const body = b4a.from('hi', 'utf8')
|
|
const hdrA = encodeUstarHeader({
|
|
name: 'a.txt',
|
|
size: body.length,
|
|
mode: 0o644,
|
|
mtime: 1,
|
|
typeflag: '0'
|
|
})
|
|
const padA = new Uint8Array((TAR_BLK - (body.length % TAR_BLK)) % TAR_BLK)
|
|
const hdrB = encodeUstarHeader({
|
|
name: 'b.txt',
|
|
size: 0,
|
|
mode: 0o644,
|
|
mtime: 1,
|
|
typeflag: '1',
|
|
linkname: 'a.txt'
|
|
})
|
|
const archive = concatTarParts([
|
|
hdrA,
|
|
body,
|
|
padA,
|
|
hdrB,
|
|
new Uint8Array(TAR_BLK * 2)
|
|
])
|
|
|
|
const errs = []
|
|
const ctx = testCtx(drive, personal)
|
|
await ctx.vfs.writeFile('hl.tar', b4a.from(archive))
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log() {},
|
|
error(s) {
|
|
errs.push(String(s))
|
|
}
|
|
}
|
|
await runTarCli(ctx, ['tar', '-xf', 'hl.tar'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(b4a.toString(await ctx.vfs.readFile('b.txt'), 'utf8'), 'hi')
|
|
|
|
await ctx.vfs.rm('a.txt', { recursive: true, force: true })
|
|
await ctx.vfs.rm('b.txt', { recursive: true, force: true })
|
|
|
|
errs.length = 0
|
|
ctx.exitCode = 0
|
|
await ctx.vfs.writeFile('hl.tar', b4a.from(archive))
|
|
const ctxStrict = testCtx(drive, personal, {
|
|
BARE_OS_VFS_STRICT_HARDLINK: '1'
|
|
})
|
|
await ctxStrict.vfs.writeFile('hl.tar', b4a.from(archive))
|
|
ctxStrict.exitCode = 0
|
|
ctxStrict.console = {
|
|
log() {},
|
|
error(s) {
|
|
errs.push(String(s))
|
|
}
|
|
}
|
|
await runTarCli(ctxStrict, ['tar', '-xf', 'hl.tar'])
|
|
t.is(ctxStrict.exitCode, 1)
|
|
t.ok(
|
|
errs.some((e) => /hard link entries not supported/i.test(e)),
|
|
'strict mode rejects hard link members'
|
|
)
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 jq from system drive', async (t) => {
|
|
const dir = testCorestoreDir('jq')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pjq'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/jq', b4a.from(await readBuiltBin('jq')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.writeFile('data.json', b4a.from('{"x":42,"name":"hi"}'))
|
|
await runBinCommand(ctx, ['jq', '.x', 'data.json'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], '42')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['jq', '-c', '.', 'data.json'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], '{"x":42,"name":"hi"}')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = '{"a":1}'
|
|
await runBinCommand(ctx, ['jq', '.a'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], '1')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 getconf and xargs from system drive', async (t) => {
|
|
const dir = testCorestoreDir('gxc')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pgx'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/getconf', b4a.from(await readBuiltBin('getconf')))
|
|
await drive.put(
|
|
'/bin/echolog',
|
|
b4a.from(`
|
|
async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`)
|
|
)
|
|
await drive.put('/bin/xargs', b4a.from(await readBuiltBin('xargs')))
|
|
await drive.put('/bin/false', b4a.from(await readBuiltBin('false')))
|
|
await drive.put('/bin/printf', b4a.from(await readBuiltBin('printf')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.runBinCommand = function (argv, ro) {
|
|
return runBinCommand(this, argv, ro)
|
|
}
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(s) {
|
|
lines.push(String(s))
|
|
}
|
|
}
|
|
await runBinCommand(ctx, ['getconf', 'PATH_MAX'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.pop(), '4096')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['getconf', 'BARE_OS_YES_MAX_LINES'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.pop(), '100000')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.bareOsPathconf = (p, n) => {
|
|
void p
|
|
if (n === '_PC_NAME_MAX') return 255
|
|
throw new Error('getconf pathconf test: unknown name')
|
|
}
|
|
await runBinCommand(ctx, ['getconf', '_PC_NAME_MAX', '/home/user'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.pop(), '255')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['getconf', 'NOT_A_REAL_CONF_NAME'])
|
|
t.is(ctx.exitCode, 1)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = 'hello\tworld\n'
|
|
await runBinCommand(ctx, ['xargs', 'echolog'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'hello world')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = 'a\0b\0'
|
|
await runBinCommand(ctx, ['xargs', '-0', '-n1', 'echolog'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'a\nb')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = 'one\ntwo\n'
|
|
await runBinCommand(ctx, ['xargs', '-I', '{}', 'echolog', 'x', '{}', 'y'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'x one y\nx two y')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = 'a\nb\n'
|
|
await runBinCommand(ctx, ['xargs', '-P2', '-n1', 'echolog'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(new Set(lines).size, 2)
|
|
t.ok(lines.includes('a'))
|
|
t.ok(lines.includes('b'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.vfs.env.BARE_OS_XARGS_MAX_PROCS = '2'
|
|
ctx.shellStdin = 'x\ny\n'
|
|
await runBinCommand(ctx, ['xargs', '-P8', '-n1', 'echolog'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.some((s) => String(s).includes('exceeds cap 2')))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = ''
|
|
await execShellLine(ctx, 'false || printf recovered')
|
|
t.is(lines.pop(), 'recovered')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 sh shebang strip and symlink script path', async (t) => {
|
|
const dir = testCorestoreDir('shshe')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('psh'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/sh', b4a.from(await readBuiltBin('sh')))
|
|
await drive.put(
|
|
'/bin/echo',
|
|
b4a.from(`async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`)
|
|
)
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.runBinCommand = function (argv, ro) {
|
|
return runBinCommand(this, argv, ro)
|
|
}
|
|
ctx.execLine = (line, opts) => execShellLine(ctx, line)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(s) {
|
|
lines.push(String(s))
|
|
}
|
|
}
|
|
await ctx.vfs.chdir('/home/user')
|
|
await ctx.vfs.writeFile(
|
|
'real.sh',
|
|
b4a.from('#!/bin/sh\necho shebang-ok\n')
|
|
)
|
|
await ctx.vfs.symlink('real.sh', 'via.sh')
|
|
await runBinCommand(ctx, ['sh', 'via.sh'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(lines.some((l) => String(l).includes('shebang-ok')))
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-2 cat env touch mkdir', async (t) => {
|
|
const dir = testCorestoreDir('tier2tem')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pt2'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
for (const name of ['cat', 'env', 'touch', 'mkdir', 'printf']) {
|
|
await drive.put('/bin/' + name, b4a.from(await readBuiltBin(name)))
|
|
}
|
|
const catOut = []
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal, { MARK: 'parent', HELLO: 'world' })
|
|
ctx.exitCode = 0
|
|
ctx.runBinCommand = function (argv, o) {
|
|
return runBinCommand(this, argv, o)
|
|
}
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(s) {
|
|
lines.push('e:' + String(s))
|
|
}
|
|
}
|
|
await ctx.vfs.chdir('/home/user')
|
|
await ctx.vfs.writeFile('plain.txt', b4a.from('a\nb\n'))
|
|
|
|
const captureCat = async (argv) => {
|
|
catOut.length = 0
|
|
const prevWrite = process.stdout.write
|
|
process.stdout.write = (chunk) => {
|
|
catOut.push(
|
|
typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8')
|
|
)
|
|
return true
|
|
}
|
|
try {
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, argv)
|
|
} finally {
|
|
process.stdout.write = prevWrite
|
|
}
|
|
}
|
|
|
|
await captureCat(['cat', '-n', 'plain.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(catOut.join(''), ' 1\ta\n 2\tb\n')
|
|
|
|
await captureCat(['cat', '-A', 'plain.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(catOut.join(''), 'a$\nb$\n')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, [
|
|
'env',
|
|
'-i',
|
|
'PATH=/bin',
|
|
'HELLO=there',
|
|
'printf',
|
|
'%s',
|
|
'ok'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'ok')
|
|
t.is(ctx.vfs.env.MARK, 'parent')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['touch', '-d', '@3600', 'ts.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
const stTs = await ctx.vfs.lstat('ts.txt')
|
|
t.is(stTs && stTs.mtimeMs, 3600000)
|
|
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['touch', 'ref.txt'])
|
|
const stRef = await ctx.vfs.lstat('ref.txt')
|
|
t.ok(stRef && typeof stRef.mtimeMs === 'number')
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['touch', '-r', 'ref.txt', 'ts2.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
const st2 = await ctx.vfs.lstat('ts2.txt')
|
|
t.is(st2 && st2.mtimeMs, stRef.mtimeMs)
|
|
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['mkdir', '-m', '700', 'secret_dir'])
|
|
t.is(ctx.exitCode, 0)
|
|
const stD = await ctx.vfs.lstat('secret_dir')
|
|
t.ok(stD && (stD.mode & 0o777) === 0o700)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('printenv -0 uses ctx.bareOsBinWrite when stdout is unavailable', async (t) => {
|
|
const dir = testCorestoreDir('binw')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pbin'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/printenv', b4a.from(await readBuiltBin('printenv')))
|
|
const raw = []
|
|
const ctx = testCtx(drive, personal, { ZZ: 'z', AA: 'a' })
|
|
ctx.exitCode = 0
|
|
ctx.runBinCommand = function (argv, o) {
|
|
return runBinCommand(this, argv, o)
|
|
}
|
|
ctx.bareOsBinWrite = (u) => {
|
|
raw.push(b4a.toString(u instanceof Uint8Array ? u : new Uint8Array(u)))
|
|
}
|
|
ctx.console = { log() {}, error() {} }
|
|
await runBinCommand(ctx, ['printenv', '-0', 'AA', 'ZZ'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(raw.join(''), 'a\0z\0')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('sed and awk golden one-liners', async (t) => {
|
|
const dir = testCorestoreDir('sawk')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('psawk'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/sed', b4a.from(await readBuiltBin('sed')))
|
|
await drive.put('/bin/awk', b4a.from(await readBuiltBin('awk')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error() {}
|
|
}
|
|
await ctx.vfs.chdir('/home/user')
|
|
await ctx.vfs.writeFile('f.txt', b4a.from('abc\ndef\n'))
|
|
await runBinCommand(ctx, ['sed', '-e', 's/a/X/', 'f.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), 'Xbc\ndef')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['awk', '{ print NF }', 'f.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.join('\n'), '1\n1')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('curl delegated from booter with stub fetch', async (t) => {
|
|
const dir = testCorestoreDir('curl')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pcurl'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(s) {
|
|
lines.push('e:' + String(s))
|
|
}
|
|
}
|
|
/** @type {RequestInit | undefined} */
|
|
let curlInitHello
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlInitHello = init
|
|
return new Response('hello', {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'text/plain' }
|
|
})
|
|
}
|
|
await runBinCommand(ctx, ['curl', 'https://stub.example/x'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], 'hello')
|
|
t.is(headerFromInit(curlInitHello, 'User-Agent'), DEFAULT_CURL_USER_AGENT)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async () => new Response('', { status: 404 })
|
|
await runBinCommand(ctx, ['curl', '-f', '-s', 'https://stub.example/missing'])
|
|
t.is(ctx.exitCode, 22)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
let curlSeenUrl = ''
|
|
/** @type {RequestInit | undefined} */
|
|
let curlSeenInit
|
|
ctx.httpFetch = async (url, init) => {
|
|
curlSeenUrl = String(url)
|
|
curlSeenInit = init
|
|
return new Response('', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, ['curl', 'example.com', '-IL'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(curlSeenUrl, 'http://example.com')
|
|
t.is(curlSeenInit && curlSeenInit.method, 'HEAD')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
/** @type {{ url: string, method: string }[]} */
|
|
const curlHopCalls = []
|
|
ctx.httpFetch = async (url, init) => {
|
|
const u = String(url)
|
|
const method = String(init?.method || 'GET')
|
|
curlHopCalls.push({ url: u, method })
|
|
if (method === 'HEAD' && u === 'http://hop.example/start') {
|
|
return new Response('', {
|
|
status: 302,
|
|
headers: { Location: 'https://hop.example/done' }
|
|
})
|
|
}
|
|
if (method === 'GET' && u === 'https://hop.example/done') {
|
|
return new Response('secret-body', {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'text/plain' }
|
|
})
|
|
}
|
|
return new Response('unexpected', { status: 500 })
|
|
}
|
|
await runBinCommand(ctx, ['curl', '-IL', 'http://hop.example/start'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(curlHopCalls.length, 2)
|
|
t.is(curlHopCalls[0].method, 'HEAD')
|
|
t.is(curlHopCalls[1].method, 'GET')
|
|
t.ok(lines[0].includes('200'))
|
|
t.ok(!lines[0].includes('secret-body'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async (url) => {
|
|
curlSeenUrl = String(url)
|
|
return new Response('', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, ['curl', '-I', '//stub.example/p'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(curlSeenUrl, 'https://stub.example/p')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
/** @type {RequestInit | undefined} */
|
|
let curlInitUa
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlInitUa = init
|
|
return new Response('x', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-A',
|
|
'CustomCurlUA/9',
|
|
'https://stub.example/ua'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(headerFromInit(curlInitUa, 'User-Agent'), 'CustomCurlUA/9')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlInitUa = init
|
|
return new Response('x', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-H',
|
|
'User-Agent: from-header',
|
|
'-A',
|
|
'from-flag',
|
|
'https://stub.example/ua2'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(headerFromInit(curlInitUa, 'User-Agent'), 'from-flag')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlInitUa = init
|
|
return new Response('x', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-A',
|
|
'from-flag',
|
|
'-H',
|
|
'User-Agent: from-header',
|
|
'https://stub.example/ua3'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(headerFromInit(curlInitUa, 'User-Agent'), 'from-header')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async () =>
|
|
new Response('body-o', {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'text/plain' }
|
|
})
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-sO',
|
|
'https://stub.example/dir/named.txt'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
const named = await ctx.vfs.readFile('named.txt')
|
|
t.is(ctx.b4a.toString(named), 'body-o')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['curl', '-o', 'x', '-O', 'https://stub.example/y'])
|
|
t.is(ctx.exitCode, 2)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
/** @type {{ url: string, method: string }[]} */
|
|
const curlRedirectCountCalls = []
|
|
ctx.httpFetch = async (url, init) => {
|
|
const u = String(url)
|
|
const method = String(init?.method || 'GET')
|
|
curlRedirectCountCalls.push({ url: u, method })
|
|
if (method === 'HEAD' && u === 'http://rc.example/a') {
|
|
return new Response('', {
|
|
status: 302,
|
|
headers: { Location: 'https://rc.example/b' }
|
|
})
|
|
}
|
|
if (method === 'GET' && u === 'https://rc.example/b') {
|
|
return new Response('', { status: 200 })
|
|
}
|
|
return new Response('bad', { status: 500 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-s',
|
|
'-w',
|
|
'%{num_redirects}',
|
|
'-o',
|
|
'hdr.dump',
|
|
'-IL',
|
|
'http://rc.example/a'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[lines.length - 1], '1')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
/** @type {RequestInit | undefined} */
|
|
let curlCookieInit
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlCookieInit = init
|
|
return new Response('c', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-b',
|
|
'sid=abc',
|
|
'https://stub.example/cookie-inline'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(headerFromInit(curlCookieInit, 'Cookie'), 'sid=abc')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await ctx.vfs.writeFile(
|
|
'/home/user/cookies.json',
|
|
b4a.from(JSON.stringify({ 'stub.example': { x: 'y' } }), 'utf8')
|
|
)
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlCookieInit = init
|
|
return new Response('c', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-b',
|
|
'/home/user/cookies.json',
|
|
'https://stub.example/cookie-file'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(String(headerFromInit(curlCookieInit, 'Cookie')).includes('x=y'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async () =>
|
|
new Response('z', {
|
|
status: 200,
|
|
headers: { 'Set-Cookie': 'sess=hello; Path=/' }
|
|
})
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-c',
|
|
'/home/user/jar.json',
|
|
'https://stub.example/jar'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
const jraw = await ctx.vfs.readFile('/home/user/jar.json')
|
|
t.ok(jraw && ctx.b4a.toString(jraw).includes('sess'))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
/** @type {{ insecure?: boolean, caPem?: string } | null} */
|
|
let curlTlsSeen = null
|
|
ctx.httpFetch = async (_url, init) => {
|
|
curlTlsSeen = init && init.bareOsCurlTls
|
|
return new Response('z', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, ['curl', '-k', 'https://stub.example/insecure'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(curlTlsSeen && curlTlsSeen.insecure === true)
|
|
t.is(lines[0], 'z')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await ctx.vfs.writeFile(
|
|
'/home/user/ca-test.pem',
|
|
b4a.from('-----BEGIN CERTIFICATE-----\nTEST\n-----END CERTIFICATE-----\n')
|
|
)
|
|
let cacertInit = null
|
|
ctx.httpFetch = async (_url, init) => {
|
|
cacertInit = init && init.bareOsCurlTls
|
|
return new Response('pem-ok', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'--cacert',
|
|
'/home/user/ca-test.pem',
|
|
'https://stub.example/ca'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(
|
|
cacertInit &&
|
|
typeof cacertInit.caPem === 'string' &&
|
|
cacertInit.caPem.includes('BEGIN CERTIFICATE')
|
|
)
|
|
t.is(lines[0], 'pem-ok')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('curl --connect-timeout aborts hanging stub fetch', async (t) => {
|
|
const dir = testCorestoreDir('curlcto')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pccto'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = { log() {}, error() {} }
|
|
ctx.httpFetch = async (_url, init) =>
|
|
new Promise((_resolve, reject) => {
|
|
const sig = init && init.signal
|
|
if (sig && sig.aborted) {
|
|
reject(new Error('The operation was aborted'))
|
|
return
|
|
}
|
|
if (sig) {
|
|
sig.addEventListener('abort', () =>
|
|
reject(new Error('The operation was aborted'))
|
|
)
|
|
}
|
|
})
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'--connect-timeout',
|
|
'1',
|
|
'https://stub.example/hang'
|
|
])
|
|
t.is(ctx.exitCode, 7)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('wget delegated from booter with stub fetch', async (t) => {
|
|
const dir = testCorestoreDir('wget')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pwget'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(s) {
|
|
lines.push('e:' + String(s))
|
|
}
|
|
}
|
|
/** @type {RequestInit | undefined} */
|
|
let wgetInitPayload
|
|
ctx.httpFetch = async (_url, init) => {
|
|
wgetInitPayload = init
|
|
return new Response('payload', {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'text/plain' }
|
|
})
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'wget',
|
|
'-O',
|
|
'saved.txt',
|
|
'-q',
|
|
'https://stub.example/a'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(headerFromInit(wgetInitPayload, 'User-Agent'), DEFAULT_WGET_USER_AGENT)
|
|
const out = await ctx.vfs.readFile('saved.txt')
|
|
t.ok(out)
|
|
t.is(ctx.b4a.toString(out), 'payload')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async () => new Response('', { status: 500 })
|
|
await runBinCommand(ctx, ['wget', '-q', 'https://stub.example/err'])
|
|
t.is(ctx.exitCode, 8)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async () => new Response('stdout-body', { status: 200 })
|
|
await runBinCommand(ctx, ['wget', '-O', '-', '-q', 'https://stub.example/b'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines[0], 'stdout-body')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
let wgetSeenUrl = ''
|
|
ctx.httpFetch = async (url) => {
|
|
wgetSeenUrl = String(url)
|
|
return new Response('p2', {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'text/plain' }
|
|
})
|
|
}
|
|
await runBinCommand(ctx, ['wget', 'stub.example/z', '-qO', 'w2.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(wgetSeenUrl, 'http://stub.example/z')
|
|
const w2 = await ctx.vfs.readFile('w2.txt')
|
|
t.ok(w2)
|
|
t.is(ctx.b4a.toString(w2), 'p2')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
/** @type {RequestInit | undefined} */
|
|
let wgetInitU
|
|
ctx.httpFetch = async (_url, init) => {
|
|
wgetInitU = init
|
|
return new Response('u', { status: 200 })
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'wget',
|
|
'-q',
|
|
'-U',
|
|
'WgetCustom/1',
|
|
'-O',
|
|
'u.txt',
|
|
'https://stub.example/u'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(headerFromInit(wgetInitU, 'User-Agent'), 'WgetCustom/1')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['wget', '-c', '-O', '-', 'https://stub.example/n'])
|
|
t.is(ctx.exitCode, 2)
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await ctx.vfs.writeFile('partial.bin', b4a.from('ab'))
|
|
ctx.httpFetch = async (_url, init) => {
|
|
t.is(headerFromInit(init, 'Range'), 'bytes=2-')
|
|
return new Response('cde', {
|
|
status: 206,
|
|
headers: { 'Content-Type': 'application/octet-stream' }
|
|
})
|
|
}
|
|
await runBinCommand(ctx, [
|
|
'wget',
|
|
'-q',
|
|
'-c',
|
|
'-O',
|
|
'partial.bin',
|
|
'https://stub.example/resume'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
const merged = await ctx.vfs.readFile('partial.bin')
|
|
t.is(ctx.b4a.toString(merged), 'abcde')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await ctx.vfs.writeFile('part.txt', b4a.from('ab'))
|
|
ctx.httpFetch = async (_url, init) => {
|
|
t.is(headerFromInit(init, 'Range'), 'bytes=2-')
|
|
return new Response('cd', { status: 206 })
|
|
}
|
|
await runBinCommand(ctx, ['wget', '-qc', 'http://stub.example/blob/part.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
const partMerged = await ctx.vfs.readFile('part.txt')
|
|
t.is(ctx.b4a.toString(partMerged), 'abcd')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('tier-1 rm -rf removes directory tree on personal drive', async (t) => {
|
|
const dir = testCorestoreDir('rmrf')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('prm'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/rm', b4a.from(await readBuiltBin('rm')))
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = { log() {}, error() {} }
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', 'nest/leaf/x.txt'),
|
|
b4a.from('x')
|
|
)
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', 'nest/other/y.txt'),
|
|
b4a.from('y')
|
|
)
|
|
t.ok((await ctx.vfs.readdir('nest')).includes('leaf'))
|
|
await runBinCommand(ctx, ['rm', '-rf', 'nest'])
|
|
t.is(ctx.exitCode, 0)
|
|
const after = await ctx.vfs.readdir('.')
|
|
t.ok(!after.includes('nest'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('git-fs-adapter mkdir recursive and readdir hides .bareos_empty', async (t) => {
|
|
const dir = testCorestoreDir('gitfs')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pgf'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(drive, personal)
|
|
const fs = createGitFsFromVfs(ctx.vfs)
|
|
const base = '/home/user/gittest'
|
|
await fs.promises.mkdir(path.posix.join(base, 'a', 'b'), { recursive: true })
|
|
const inA = await fs.promises.readdir(path.posix.join(base, 'a'))
|
|
t.is(inA.indexOf('.bareos_empty'), -1)
|
|
t.ok(inA.includes('b'))
|
|
const top = await fs.promises.readdir(base)
|
|
t.ok(top.includes('a'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand delegates git to booter (ignores /bin/git script body)', async (t) => {
|
|
const dir = testCorestoreDir('gitdel')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pgd'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put(
|
|
'/bin/git',
|
|
b4a.from(
|
|
`async function run() { throw new Error('eval git should not run') }`
|
|
)
|
|
)
|
|
const logs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
await runBinCommand(ctx, ['git', 'version'])
|
|
t.ok(logs.some((l) => /isomorphic-git/i.test(l)))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runBinCommand runs ./git from cwd instead of booter delegate', async (t) => {
|
|
const dir = testCorestoreDir('gitlocal')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pgl'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await personal.put(
|
|
personalHomeBacking('/home/user', 'git'),
|
|
b4a.from(`
|
|
async function run(ctx) {
|
|
ctx.out.push('local-git-script')
|
|
}
|
|
`)
|
|
)
|
|
const out = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.out = out
|
|
await runBinCommand(ctx, ['./git'])
|
|
t.is(out[0], 'local-git-script')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('runGitCli init and status on personal drive', async (t) => {
|
|
const dir = testCorestoreDir('gitcmd')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pgc'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const logs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
await runGitCli(ctx, ['git', 'init', '-C', '/home/user/myrepo'])
|
|
t.ok(logs.some((l) => /initialized|git repository/i.test(l)))
|
|
logs.length = 0
|
|
await runGitCli(ctx, ['git', '-C', '/home/user/myrepo', 'status'])
|
|
t.ok(logs.length > 0)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('git clean -fd removes untracked files only', async (t) => {
|
|
const dir = testCorestoreDir('gitclean')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pgcl'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = { log() {}, error() {} }
|
|
await runGitCli(ctx, ['git', 'init', '-C', '/home/user/clrepo'])
|
|
await ctx.vfs.writeFile('/home/user/clrepo/a.txt', b4a.from('a'))
|
|
await runGitCli(ctx, ['git', '-C', '/home/user/clrepo', 'add', 'a.txt'])
|
|
await runGitCli(ctx, [
|
|
'git',
|
|
'-C',
|
|
'/home/user/clrepo',
|
|
'commit',
|
|
'-m',
|
|
'init'
|
|
])
|
|
await ctx.vfs.writeFile('/home/user/clrepo/junk.txt', b4a.from('j'))
|
|
await runGitCli(ctx, ['git', '-C', '/home/user/clrepo', 'clean', '-fd'])
|
|
t.is(await ctx.vfs.readFile('/home/user/clrepo/junk.txt'), null)
|
|
t.ok(await ctx.vfs.readFile('/home/user/clrepo/a.txt'))
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('curl -O -J saves Content-Disposition filename', async (t) => {
|
|
const dir = testCorestoreDir('curlj')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pcj'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.httpFetch = async () =>
|
|
new Response(b4a.from('payload'), {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Disposition': 'attachment; filename="from-server.bin"'
|
|
}
|
|
})
|
|
await runBinCommand(ctx, [
|
|
'curl',
|
|
'-s',
|
|
'-O',
|
|
'-J',
|
|
'https://stub.example/blob'
|
|
])
|
|
t.is(ctx.exitCode, 0)
|
|
const out = await ctx.vfs.readFile('/home/user/from-server.bin')
|
|
t.ok(out)
|
|
t.is(ctx.b4a.toString(out), 'payload')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('bare-os-ipc fanout publishes to subscribers', async (t) => {
|
|
const ipc = createBareOsIpc({ maxFifoBytes: 65536 })
|
|
const a = ipc.fanoutSubscribe('news')
|
|
const b = ipc.fanoutSubscribe('news')
|
|
ipc.fanoutPublish('news', b4a.from('x', 'utf8'))
|
|
t.is(b4a.toString(await a.take()), 'x')
|
|
t.is(b4a.toString(await b.take()), 'x')
|
|
a.dispose()
|
|
b.dispose()
|
|
})
|
|
|
|
test('bare-os-ipc pushJson requires token when configured', async (t) => {
|
|
const ipc = createBareOsIpc({ ipcRpcToken: 'secret' })
|
|
ipc.create('q')
|
|
t.exception(() => ipc.pushJson('q', { x: 1 }), /token/)
|
|
ipc.pushJson('q', { x: 1, bareOsIpcToken: 'secret' })
|
|
const j = await ipc.takeJson('q')
|
|
t.is(j.x, 1)
|
|
})
|
|
|
|
test('bareOsHttpUrlAllowed allowlist and denylist', async (t) => {
|
|
t.ok(bareOsHttpUrlAllowed('https://a.example/x', { allow: [], deny: [] }).ok)
|
|
t.ok(
|
|
!bareOsHttpUrlAllowed('https://evil.com/', {
|
|
allow: ['a.example'],
|
|
deny: []
|
|
}).ok
|
|
)
|
|
t.ok(
|
|
bareOsHttpUrlAllowed('https://a.example/', {
|
|
allow: ['a.example'],
|
|
deny: []
|
|
}).ok
|
|
)
|
|
t.ok(
|
|
!bareOsHttpUrlAllowed('https://a.example/', {
|
|
allow: ['a.example'],
|
|
deny: ['a.example']
|
|
}).ok
|
|
)
|
|
})
|
|
|
|
test('raceWithAbortAndTimeout rejects on timeout', async (t) => {
|
|
try {
|
|
await raceWithAbortAndTimeout(
|
|
new Promise(() => {}),
|
|
{ timeoutMs: 30 },
|
|
'hang'
|
|
)
|
|
t.ok(false, 'expected timeout')
|
|
} catch (e) {
|
|
t.ok(/timed out/.test(String(e && e.message)))
|
|
}
|
|
})
|
|
|
|
test('kernel share/man/man.json page count matches coreutils + extras + handbook + devguide', async (t) => {
|
|
const manPath = path.join(__dirname, '../../kernel/share/man/man.json')
|
|
const raw = JSON.parse(await readFile(manPath, 'utf8'))
|
|
const { COREUTILS_COMMANDS, MAN_EXTRA_PAGES } =
|
|
await import('../bare-os-coreutils/lib/commands.mjs')
|
|
const handbookDir = path.join(__dirname, '../../handbook')
|
|
const devguideDir = path.join(__dirname, '../../developer-guide')
|
|
const handbookMd = (await readdir(handbookDir)).filter((f) =>
|
|
f.endsWith('.md')
|
|
).length
|
|
const devguideMd = (await readdir(devguideDir)).filter((f) =>
|
|
f.endsWith('.md')
|
|
).length
|
|
t.is(
|
|
raw.pages.length,
|
|
COREUTILS_COMMANDS.length + MAN_EXTRA_PAGES.length + handbookMd + devguideMd
|
|
)
|
|
t.ok(Array.isArray(raw.apropos) && raw.apropos.length > 0)
|
|
t.is(typeof raw.index.ls, 'number')
|
|
t.is(typeof raw.index.handbook, 'number')
|
|
t.is(typeof raw.index.devguide, 'number')
|
|
const hb = raw.pages[raw.index.handbook]
|
|
t.is(hb.section, 7)
|
|
t.is(hb.name, 'bare-os-handbook')
|
|
const dg = raw.pages[raw.index.devguide]
|
|
t.is(dg.section, 7)
|
|
t.is(dg.name, 'bare-os-developer-guide')
|
|
})
|
|
|
|
test('runBinCommand man ls prints manual text', async (t) => {
|
|
const dir = testCorestoreDir('mann')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pmn'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const manPath = path.join(__dirname, '../../kernel/share/man/man.json')
|
|
await drive.put(
|
|
'/share/man/man.json',
|
|
b4a.from(await readFile(manPath, 'utf8'))
|
|
)
|
|
await drive.put('/bin/man', b4a.from(await readBuiltBin('man')))
|
|
const logs = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.console = {
|
|
log: (...a) => logs.push(a.join(' ')),
|
|
error: (...a) => logs.push(a.join(' '))
|
|
}
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['man', 'ls'])
|
|
t.is(ctx.exitCode, 0)
|
|
const text = logs.join('\n')
|
|
t.ok(text.includes('SYNOPSIS'))
|
|
t.ok(text.includes('EXAMPLES'))
|
|
t.ok(text.includes('ls'))
|
|
logs.length = 0
|
|
await runBinCommand(ctx, ['man', '-w'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(logs.join('\n').trim(), '/share/man/man.json')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('coreutils sed and awk fixture corpus', async (t) => {
|
|
const corpusPath = path.join(
|
|
__dirname,
|
|
'../bare-os-coreutils/fixtures/sed-awk-corpus.json'
|
|
)
|
|
const corpus = JSON.parse(await readFile(corpusPath, 'utf8'))
|
|
const dir = testCorestoreDir('sedawkcorpus')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('psac'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
await drive.put('/bin/sed', b4a.from(await readBuiltBin('sed')))
|
|
await drive.put('/bin/awk', b4a.from(await readBuiltBin('awk')))
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(...a) {
|
|
lines.push(a.join(' '))
|
|
}
|
|
}
|
|
function corpusNormLines(logs) {
|
|
return logs
|
|
.flatMap((l) => String(l).split('\n'))
|
|
.filter((x) => x.length > 0)
|
|
}
|
|
for (const c of corpus.sed) {
|
|
await ctx.vfs.writeFile(c.file, b4a.from(c.content))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, c.argv)
|
|
t.is(ctx.exitCode, 0, 'sed ' + c.name)
|
|
t.alike(corpusNormLines(lines), c.lines, 'sed ' + c.name)
|
|
}
|
|
for (const c of corpus.awk) {
|
|
if (c.progFile) {
|
|
await ctx.vfs.writeFile(c.progFile, b4a.from(c.progContent))
|
|
}
|
|
if (c.file) await ctx.vfs.writeFile(c.file, b4a.from(c.content))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, c.argv)
|
|
t.is(ctx.exitCode, 0, 'awk ' + c.name)
|
|
t.alike(corpusNormLines(lines), c.lines, 'awk ' + c.name)
|
|
}
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('coreutils matrix: cp find sort printf uniq realpath sha256sum base64 rm -d', async (t) => {
|
|
const dir = testCorestoreDir('corematrix')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pcm'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const bins = [
|
|
'cp',
|
|
'find',
|
|
'sort',
|
|
'printf',
|
|
'uniq',
|
|
'realpath',
|
|
'sha256sum',
|
|
'base64',
|
|
'rm',
|
|
'mkdir',
|
|
'touch'
|
|
]
|
|
for (const b of bins) {
|
|
await drive.put('/bin/' + b, b4a.from(await readBuiltBin(b)))
|
|
}
|
|
await drive.put(
|
|
'/bin/_corpus_echo',
|
|
b4a.from(`async function run(ctx, argv) {
|
|
ctx.console.log(argv.slice(1).join(' '))
|
|
}
|
|
`)
|
|
)
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.runBinCommand = function (argv, ro) {
|
|
return runBinCommand(this, argv, ro)
|
|
}
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(...a) {
|
|
lines.push(a.join(' '))
|
|
}
|
|
}
|
|
|
|
await ctx.vfs.mkdir('tree', { recursive: true })
|
|
await ctx.vfs.writeFile('tree/a.txt', b4a.from('newdata'))
|
|
await ctx.vfs.writeFile('dest.txt', b4a.from('stale'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['touch', '-d', '@1', 'dest.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['touch', '-d', '@100000', 'tree/a.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['cp', '-u', 'tree/a.txt', 'dest.txt'])
|
|
t.is(ctx.exitCode, 0, 'cp -u')
|
|
t.is(ctx.b4a.toString(await ctx.vfs.readFile('dest.txt')), 'newdata')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['find', '.', '-type', 'f', '-regex', '.*\\.txt$'])
|
|
t.is(ctx.exitCode, 0, 'find -regex')
|
|
t.ok(lines.some((l) => String(l).includes('a.txt')))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['find', 'tree', '-exec', '_corpus_echo', '{}', ';'])
|
|
t.is(ctx.exitCode, 0, 'find -exec')
|
|
t.ok(lines.some((l) => /a\.txt/.test(String(l))))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
ctx.shellStdin = '3\n1\n2\n'
|
|
await runBinCommand(ctx, ['sort', '-n'])
|
|
t.is(lines.join('\n'), '1\n2\n3')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['printf', '%s-%d', 'n', '7'])
|
|
t.is(lines[0], 'n-7')
|
|
|
|
await ctx.vfs.writeFile('u.txt', b4a.from('a\na\nb\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['uniq', '-c', 'u.txt'])
|
|
t.ok(lines.some((l) => /^\s*2\s+a$/.test(String(l))))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['realpath', 'tree/a.txt'])
|
|
t.ok(/a\.txt$/.test(lines[0]))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sha256sum', 'u.txt'])
|
|
t.is(lines.length, 1)
|
|
t.ok(/^[a-f0-9]{64}\s+/.test(lines[0]))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['base64', '-w', '0', 'u.txt'])
|
|
const dec = Buffer.from(
|
|
lines.join('').replace(/\s+/g, ''),
|
|
'base64'
|
|
).toString()
|
|
t.is(dec, 'a\na\nb\n')
|
|
|
|
await ctx.vfs.mkdir('emptydir', { recursive: true })
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['rm', '-d', 'emptydir'])
|
|
t.is(ctx.exitCode, 0, 'rm -d')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('coreutils gnu-gap batch: paste tac rev md5sum expr tsort numfmt truncate install comm join', async (t) => {
|
|
const dir = testCorestoreDir('gnugap')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('ngg'))
|
|
await drive.ready()
|
|
await personal.ready()
|
|
const bins = [
|
|
'paste',
|
|
'tac',
|
|
'rev',
|
|
'md5sum',
|
|
'expr',
|
|
'tsort',
|
|
'numfmt',
|
|
'sync',
|
|
'truncate',
|
|
'install',
|
|
'unlink',
|
|
'comm',
|
|
'join',
|
|
'yes',
|
|
'cp',
|
|
'touch',
|
|
'mkdir',
|
|
'ls'
|
|
]
|
|
for (const b of bins) {
|
|
await drive.put('/bin/' + b, b4a.from(await readBuiltBin(b)))
|
|
}
|
|
const lines = []
|
|
const ctx = testCtx(drive, personal)
|
|
ctx.runBinCommand = function (argv, ro) {
|
|
return runBinCommand(this, argv, ro)
|
|
}
|
|
ctx.exitCode = 0
|
|
ctx.console = {
|
|
log(s) {
|
|
lines.push(String(s))
|
|
},
|
|
error(...a) {
|
|
lines.push(a.join(' '))
|
|
}
|
|
}
|
|
ctx.vfs.env.BARE_OS_YES_MAX_LINES = '2'
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['yes', 'ok'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(lines.filter((l) => l === 'ok').length, 2)
|
|
|
|
await ctx.vfs.writeFile('nums.txt', b4a.from('1\n2\n3\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['tac', 'nums.txt'])
|
|
t.is(lines.join('\n'), '3\n2\n1')
|
|
|
|
await ctx.vfs.writeFile('rv.txt', b4a.from('ab\ncd\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['rev', 'rv.txt'])
|
|
t.is(lines.join('\n'), 'ba\ndc')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['md5sum', 'nums.txt'])
|
|
t.ok(/^[a-f0-9]{32}\s/.test(lines[0]))
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['expr', '1', '+', '2', '*', '3'])
|
|
t.is(lines[0], '7')
|
|
|
|
await ctx.vfs.writeFile('ts.txt', b4a.from('a b\nb c\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['tsort', 'ts.txt'])
|
|
t.is(lines.join('\n'), 'a\nb\nc')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['numfmt', '--to=iec', '1024'])
|
|
t.is(lines[0], '1K')
|
|
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['sync'])
|
|
t.is(ctx.exitCode, 0)
|
|
|
|
await ctx.vfs.writeFile('tr.txt', b4a.from('hello'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['truncate', '-s', '2', 'tr.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(ctx.b4a.toString(await ctx.vfs.readFile('tr.txt')), 'he')
|
|
|
|
await ctx.vfs.writeFile('src.txt', b4a.from('data'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['install', '-m', '600', 'src.txt', 'dest.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(ctx.b4a.toString(await ctx.vfs.readFile('dest.txt')), 'data')
|
|
|
|
await ctx.vfs.writeFile('s1.txt', b4a.from('a\nb\n'))
|
|
await ctx.vfs.writeFile('s2.txt', b4a.from('a\nc\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['comm', 's1.txt', 's2.txt'])
|
|
t.ok(lines.some((l) => l.includes('\t\ta')))
|
|
|
|
await ctx.vfs.writeFile('j1.txt', b4a.from('1 x\n2 y\n'))
|
|
await ctx.vfs.writeFile('j2.txt', b4a.from('1 p\n2 q\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['join', 'j1.txt', 'j2.txt'])
|
|
t.ok(lines.some((l) => /1\s+x\s+1\s+p/.test(String(l))))
|
|
|
|
await ctx.vfs.writeFile('u.txt', b4a.from('x'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['unlink', 'u.txt'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.is(await ctx.vfs.readFile('u.txt'), null)
|
|
|
|
await ctx.vfs.writeFile('p1.txt', b4a.from('a\nb\n'))
|
|
await ctx.vfs.writeFile('p2.txt', b4a.from('1\n2\n'))
|
|
lines.length = 0
|
|
ctx.exitCode = 0
|
|
await runBinCommand(ctx, ['paste', 'p1.txt', 'p2.txt'])
|
|
t.is(lines[0], 'a\t1')
|
|
t.is(lines[1], 'b\t2')
|
|
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('verifyBootManifestEd25519 rejects invalid inputs', async (t) => {
|
|
const { verifyBootManifestEd25519 } =
|
|
await import('#bare-os-boot-manifest-sig')
|
|
const msg = b4a.from('manifest-bytes', 'utf8')
|
|
t.absent(verifyBootManifestEd25519(msg, null, ''))
|
|
t.absent(verifyBootManifestEd25519(msg, msg, '00ff'))
|
|
const z64 = '0'.repeat(64)
|
|
t.absent(verifyBootManifestEd25519(msg, new Uint8Array(64), z64))
|
|
})
|
|
|
|
test('bareOsIpc.duplexJsonRoundTrip', async (t) => {
|
|
const ipc = createBareOsIpc()
|
|
const { left, right } = ipc.createDuplexBridge('dupjx')
|
|
const respP = ipc.duplexJsonRoundTrip(right, { id: 7, method: 'ping' })
|
|
const u8 = await left.take()
|
|
const req = JSON.parse(b4a.toString(u8, 'utf8'))
|
|
t.is(req.method, 'ping')
|
|
left.push(
|
|
b4a.from(
|
|
JSON.stringify({ bareOsRpc: '2', id: 7, result: 'pong' }) + '\n',
|
|
'utf8'
|
|
)
|
|
)
|
|
const out = await respP
|
|
t.is(out.result, 'pong')
|
|
})
|
|
|
|
test('unit journal exposed under /run/bare-os/unit-journal', async (t) => {
|
|
const {
|
|
appendBareInitdJournal,
|
|
clearBareInitdJournalForTests,
|
|
getBareInitdJournalNdjson
|
|
} = await import('./lib/bare-initd-journal.js')
|
|
clearBareInitdJournalForTests()
|
|
appendBareInitdJournal('demo', { event: 'unit_test' })
|
|
const dir = testCorestoreDir('vj')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('pvj'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const env = {
|
|
HOME: '/home/guest',
|
|
PWD: '/home/guest',
|
|
PATH: '/bin',
|
|
USER: 'guest'
|
|
}
|
|
const vfs = createVfs(sys, personal, env, null, {
|
|
getUnitJournalNdjson: (u) => getBareInitdJournalNdjson(u)
|
|
})
|
|
t.ok((await vfs.readdir('/run/bare-os/unit-journal')).includes('demo.ndjson'))
|
|
const j = b4a.toString(
|
|
await vfs.readFile('/run/bare-os/unit-journal/demo.ndjson')
|
|
)
|
|
t.ok(j.includes('unit_test'))
|
|
clearBareInitdJournalForTests()
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('worker_budget proc surface schema 2 exposes cpu and class map env', async (t) => {
|
|
const j = buildBareOsReplicationOperatorSurfaceProcJson('worker_budget', {
|
|
BARE_OS_BIN_WORKER_WALL_MS_MAX: '5000',
|
|
BARE_OS_BIN_WORKER_CPU_MS_MAX: '2500',
|
|
BARE_OS_KERNEL_RUNNER_CLASS_CPU_MS_MAX_JSON: '{"textproc":100}'
|
|
})
|
|
t.is(j.schema, 2)
|
|
t.is(j.wallMsMax, '5000')
|
|
t.is(j.cpuMsMax, '2500')
|
|
t.alike(j.kernelRunnerClassCpuMsMax, { textproc: 100 })
|
|
})
|
|
|
|
test('process table snapshot includes processGroups metadata', async (t) => {
|
|
const s = bareOsProcessTableSnapshot({
|
|
env: { BARE_OS_SHELL_PIPEFAIL: '1' }
|
|
})
|
|
t.ok(s.processGroups)
|
|
t.is(s.processGroups.schema, 1)
|
|
t.ok(String(s.processGroups.killpgAnalog).includes('killpg'))
|
|
t.is(s.schemaVersion, 7)
|
|
t.ok(s.jobControlSemantics && s.jobControlSemantics.pipefail === true)
|
|
const booter = s.processes.find((p) => p && p.pid === 2)
|
|
t.ok(booter && booter.parentName === 'bare-os-kernel')
|
|
const shell = s.processes.find((p) => p && p.pid === 3)
|
|
t.ok(shell && shell.parentName === 'bare-os-booter')
|
|
t.ok(s.signalRouting && s.signalRouting.schema === 1)
|
|
t.ok(s.initdBinding)
|
|
t.ok(s.signalModel && Array.isArray(s.signalModel.sigpendingAnalog))
|
|
t.ok(s.exitStatusModel && s.exitStatusModel.schema === 1)
|
|
})
|
|
|
|
test('bare_acl other:: and mask interact like POSIX ACL classes', async (t) => {
|
|
const env = { UID: '1000', GID: '1000' }
|
|
const p1 = bareOsParseBareAclText('other::---\n')
|
|
t.ok(bareOsAclDeniesSubject(p1, env, '1000', '1000', 'read'))
|
|
|
|
const p2 = bareOsParseBareAclText(
|
|
'user:1000:rwx\nmask::r--\nother::rwx\n'
|
|
)
|
|
t.ok(bareOsAclDeniesSubject(p2, env, '1000', '1000', 'write'))
|
|
t.absent(bareOsAclDeniesSubject(p2, env, '1000', '1000', 'read'))
|
|
|
|
const p3 = bareOsParseBareAclText('user::r-x\n')
|
|
t.ok(bareOsAclDeniesSubject(p3, env, '1000', '1000', 'write'))
|
|
t.absent(bareOsAclDeniesSubject(p3, env, '1000', '1000', 'read'))
|
|
})
|
|
|
|
test('buildBareOsReplicationOperatorSurfaceProcJson corestore_snapshot', async (t) => {
|
|
const j = buildBareOsReplicationOperatorSurfaceProcJson('corestore_snapshot', {
|
|
BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG: 'ci-tag',
|
|
BARE_OS_SEED_SNAPSHOT_HINTS_JSON: '{"ok":true}',
|
|
BARE_OS_CORESTORE_SNAPSHOT_STATE_JSON: '{"cores":3,"paused":false}',
|
|
BARE_OS_CORESTORE_SNAPSHOT_PAUSED: '1',
|
|
BARE_OS_REPLICATION_PAUSED: 'true'
|
|
})
|
|
t.is(j.schema, 2)
|
|
t.is(j.snapshotTag, 'ci-tag')
|
|
t.ok(j.snapshotHintsJson && j.snapshotHintsJson.ok === true)
|
|
t.is(j.operatorSnapshotState?.cores, 3)
|
|
t.is(j.paused, true)
|
|
t.is(j.replicationPausedEnv, true)
|
|
})
|
|
|
|
test('buildBareOsReplicationOperatorSurfaceProcJson replication_operator_panel', async (t) => {
|
|
const j = buildBareOsReplicationOperatorSurfaceProcJson(
|
|
'replication_operator_panel',
|
|
{
|
|
BARE_OS_REPLICATION_OPERATOR_PANEL_JSON: '{"treeLength":42}',
|
|
BARE_OS_HYPERCORE_LENGTHS_JSON: '{"a":1}',
|
|
BARE_OS_REPLICATION_BACKPRESSURE_JSON: '{"depth":1}',
|
|
BARE_OS_DHT_STATUS_JSON: '{"ok":true}',
|
|
BARE_OS_PEER_ALLOWLIST_HEX: 'abc'
|
|
}
|
|
)
|
|
t.is(j.schema, 1)
|
|
t.ok(j.panel && j.panel.treeLength === 42)
|
|
t.ok(j.hypercoreLengths && j.hypercoreLengths.a === 1)
|
|
t.is(j.peerAllowlistHexConfigured, true)
|
|
})
|
|
|
|
test('bare-os-protocol swarm topic matches PROTOCOL_NAME', async (t) => {
|
|
const { PROTOCOL_NAME, TOPIC_STRING, topicKey } = await import(
|
|
'bare-os-protocol'
|
|
)
|
|
t.is(PROTOCOL_NAME, TOPIC_STRING)
|
|
t.is(PROTOCOL_NAME, 'bare-os-v1')
|
|
const k = topicKey()
|
|
t.is(k.length, 32)
|
|
})
|
|
|
|
test('bareOsIpc.stats includes ipcBackpressure topChannels', async (t) => {
|
|
const ipc = createBareOsIpc()
|
|
ipc.create('q1')
|
|
const u8 = b4a.from('x')
|
|
for (let i = 0; i < 5; i++) ipc.push('q1', u8)
|
|
const st = ipc.stats()
|
|
t.ok(st.ipcBackpressure && st.ipcBackpressure.schema === 1)
|
|
t.ok(Array.isArray(st.ipcBackpressure.topChannels))
|
|
t.ok(st.ipcBackpressure.topChannels.some((r) => r.name === 'q1'))
|
|
})
|
|
|
|
test('bareOsWasmKernelCompile bounds and compile path', async (t) => {
|
|
const { bareOsWasmKernelCompile } = await import('./lib/bare-os-wasm-kernel.js')
|
|
const big = new Uint8Array(600 * 1024)
|
|
const r0 = await bareOsWasmKernelCompile(big, { maxBytes: 1024 })
|
|
t.absent(r0.ok)
|
|
if (typeof WebAssembly === 'undefined' || !WebAssembly.compile) {
|
|
const r1 = await bareOsWasmKernelCompile(new Uint8Array([0x00, 0x61, 0x73, 0x6d]))
|
|
t.absent(r1.ok)
|
|
return
|
|
}
|
|
const minimal = new Uint8Array([
|
|
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00
|
|
])
|
|
const r2 = await bareOsWasmKernelCompile(minimal)
|
|
t.ok(r2.ok === true || r2.ok === false)
|
|
})
|
|
|
|
test('bareOsWasmKernelInstantiate exposes wall clock import with syscall imports', async (t) => {
|
|
const { bareOsWasmKernelInstantiate } = await import('./lib/bare-os-wasm-kernel.js')
|
|
if (typeof WebAssembly === 'undefined' || !WebAssembly.compile) {
|
|
t.pass('skip: no WebAssembly')
|
|
return
|
|
}
|
|
const wasmMinimal = new Uint8Array([
|
|
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00
|
|
])
|
|
const ctx = { bareOsPathconf: () => '4096' }
|
|
const r = await bareOsWasmKernelInstantiate(wasmMinimal, ctx, {
|
|
wasmSyscallImports: true,
|
|
shellEnv: { BARE_OS_WASM_KERNEL_SYSCALL: '1' }
|
|
})
|
|
if (!r.ok) {
|
|
t.pass('instantiate may fail on minimal module; wall import path exercised when ok')
|
|
return
|
|
}
|
|
t.ok(r.wasmWallClockMs32Import)
|
|
t.ok(r.wasmPathconfImport)
|
|
})
|
|
|
|
test('bareOsAppendVaultRotationCheckpoint appends vault_save row', async (t) => {
|
|
const { bareOsAppendVaultRotationCheckpoint } = await import(
|
|
'./lib/bare-os-vault-rotation-audit.js'
|
|
)
|
|
/** @type {{ p: string, buf: Uint8Array }[]} */
|
|
const puts = []
|
|
const drive = {
|
|
async get(p) {
|
|
if (p === '/.bare/vault-rotation-audit.ndjson') return null
|
|
return null
|
|
},
|
|
async put(p, buf) {
|
|
puts.push({ p, buf: buf instanceof Uint8Array ? buf : b4a.from(buf) })
|
|
}
|
|
}
|
|
const ctx = { personalDrive: drive }
|
|
await bareOsAppendVaultRotationCheckpoint(ctx, { kind: 'vault_save', fileCount: 2 })
|
|
const auditPut = puts.filter((x) => x.p === '/.bare/vault-rotation-audit.ndjson')
|
|
t.is(auditPut.length, 1)
|
|
const last = JSON.parse(b4a.toString(auditPut[0].buf).trim())
|
|
t.is(last.kind, 'vault_save')
|
|
t.is(last.fileCount, 2)
|
|
})
|
|
|
|
test('protomux and hyperswarm versions match repo lock contract', async (t) => {
|
|
const { readFileSync } = await import('node:fs')
|
|
const lockPath = path.join(__dirname, '../../package-lock.json')
|
|
const lock = JSON.parse(readFileSync(lockPath, 'utf8'))
|
|
const pm = lock.packages?.['node_modules/protomux']?.version
|
|
const hs = lock.packages?.['node_modules/hyperswarm']?.version
|
|
t.ok(pm && hs)
|
|
const snapPath = path.join(__dirname, 'fixtures/protomux-hyperswarm-lock.json')
|
|
const snap = JSON.parse(readFileSync(snapPath, 'utf8'))
|
|
t.is(pm, snap.protomux)
|
|
t.is(hs, snap.hyperswarm)
|
|
})
|
|
|
|
test('kernel extension topological order matches guest Kahn tie-break', async (t) => {
|
|
const entries = [
|
|
{ file: 'b.json', extId: 'b', scripts: ['/x'], dependsOn: ['a'] },
|
|
{ file: 'a.json', extId: 'a', scripts: ['/y'], dependsOn: [] }
|
|
]
|
|
const topo = topologicalOrderKernelExtensions(entries)
|
|
t.ok(topo.ok)
|
|
t.is(
|
|
topo.ordered.map((e) => e.file).join(','),
|
|
'a.json,b.json',
|
|
'filename order breaks ties; dependsOn edge b→a'
|
|
)
|
|
})
|
|
|
|
test('kernel extension resolver detects dependency cycle', async (t) => {
|
|
const entries = [
|
|
{ file: 'a.json', extId: 'a', scripts: ['/x'], dependsOn: ['b'] },
|
|
{ file: 'b.json', extId: 'b', scripts: ['/y'], dependsOn: ['a'] }
|
|
]
|
|
const topo = topologicalOrderKernelExtensions(entries)
|
|
t.absent(topo.ok)
|
|
t.ok(
|
|
/** @type {{ cycleExtIds: string[] }} */ (topo).cycleExtIds.length >= 2
|
|
)
|
|
})
|
|
|
|
test('protomux alias registry snapshot matches proc wire contract shape', async (t) => {
|
|
const reg = createBareOsProtomuxAliasRegistry()
|
|
reg.register('guestChannel', 'wireChannel')
|
|
const rt = reg.snapshot()
|
|
t.is(rt.schema, 2)
|
|
t.is(rt.aliases.guestChannel, 'wireChannel')
|
|
t.ok(Array.isArray(rt.changeLogTail))
|
|
t.ok(rt.reverseIndex && typeof rt.reverseIndex === 'object')
|
|
})
|
|
|
|
test('createBareOsIpc enforces maxChannels quota', async (t) => {
|
|
const ipc = createBareOsIpc({ maxChannels: 2, maxFifoBytes: 4096 })
|
|
ipc.create('a')
|
|
ipc.create('b')
|
|
let threw = false
|
|
try {
|
|
ipc.create('c')
|
|
} catch {
|
|
threw = true
|
|
}
|
|
t.ok(threw)
|
|
const st = ipc.stats()
|
|
t.is(st.maxChannels, 2)
|
|
t.ok(st.telemetry.fifoCreateDeniedQuota >= 1)
|
|
})
|
|
|
|
test('vfs union write deny + bareOsEvictWarmReadLogicalPath selective eviction', async (t) => {
|
|
const dir = testCorestoreDir('unionvfs')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('uv'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
await sys.put('/bin/u1', b4a.from('a'))
|
|
await sys.put('/bin/u2', b4a.from('b'))
|
|
const warmRef = { current: null }
|
|
const env = {
|
|
HOME: '/home/g',
|
|
PWD: '/',
|
|
PATH: '/bin',
|
|
BARE_OS_VFS_BIN_CACHE: '1'
|
|
}
|
|
const vfs = createVfs(sys, personal, env, null, {
|
|
unionReadPrefixes: ['/bin'],
|
|
unionWriteDenyPrefixes: ['/bin'],
|
|
warmReadCacheStatsRef: warmRef
|
|
})
|
|
let denied = false
|
|
try {
|
|
await vfs.writeFile('/bin/z', b4a.from('x'))
|
|
} catch {
|
|
denied = true
|
|
}
|
|
t.ok(denied, 'union write deny blocks /bin write')
|
|
await vfs.readFile('/bin/u1')
|
|
await vfs.readFile('/bin/u2')
|
|
const hits = warmRef.current?.hits ?? 0
|
|
vfs.bareOsEvictWarmReadLogicalPath('/bin/u1')
|
|
const m0 = warmRef.current?.misses ?? 0
|
|
await vfs.readFile('/bin/u1')
|
|
t.ok((warmRef.current?.misses ?? 0) > m0, 'u1 evicted')
|
|
const hitsU2 = warmRef.current?.hits ?? 0
|
|
await vfs.readFile('/bin/u2')
|
|
t.ok((warmRef.current?.hits ?? 0) > hitsU2, 'u2 still warm')
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('disk.os replication_operator_sketch mirrors blind v3 and hyperblobs env JSON', async (t) => {
|
|
const dir = testCorestoreDir('diskosenv')
|
|
const store = new Corestore(dir)
|
|
const drive = new Hyperdrive(store)
|
|
await drive.ready()
|
|
const prevB = process.env.BARE_OS_BLIND_PEER_TOPOLOGY_V3_JSON
|
|
const prevH = process.env.BARE_OS_HYPERBLOBS_STATS_JSON
|
|
process.env.BARE_OS_BLIND_PEER_TOPOLOGY_V3_JSON = JSON.stringify({
|
|
schema: 3,
|
|
hint: 't'
|
|
})
|
|
process.env.BARE_OS_HYPERBLOBS_STATS_JSON = JSON.stringify({ chunkCount: 4 })
|
|
try {
|
|
const bridge = createBareOsDiskOsBridge({
|
|
drive,
|
|
bareOsIpc: { list: () => [] },
|
|
ctxApiVersion: BARE_OS_CTX_API_VERSION,
|
|
systemRevision: null,
|
|
bootStartedMs: Date.now()
|
|
})
|
|
const op = await bridge.execRpc('bare_os', 'replication_operator_sketch', [])
|
|
const oj = JSON.parse(op)
|
|
t.is(oj.schema, 4)
|
|
t.is(oj.blindTopologySketchV3?.schema, 3)
|
|
t.is(oj.hyperblobsDedupSketch?.chunkCount, 4)
|
|
} finally {
|
|
if (prevB === undefined) delete process.env.BARE_OS_BLIND_PEER_TOPOLOGY_V3_JSON
|
|
else process.env.BARE_OS_BLIND_PEER_TOPOLOGY_V3_JSON = prevB
|
|
if (prevH === undefined) delete process.env.BARE_OS_HYPERBLOBS_STATS_JSON
|
|
else process.env.BARE_OS_HYPERBLOBS_STATS_JSON = prevH
|
|
}
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('ctx bareOsRunCurlCli works when host delegates curl is disabled', async (t) => {
|
|
const dir = testCorestoreDir('curlctx')
|
|
const store = new Corestore(dir)
|
|
const sys = new Hyperdrive(store)
|
|
const personal = new Hyperdrive(store.namespace('cc'))
|
|
await sys.ready()
|
|
await personal.ready()
|
|
const ctx = testCtx(sys, personal, {
|
|
BARE_OS_DELEGATE_ALLOW: 'git',
|
|
HOME: '/home/g',
|
|
PWD: '/',
|
|
PATH: '/bin'
|
|
})
|
|
let ran = false
|
|
ctx.bareOsRunCurlCli = async () => {
|
|
ran = true
|
|
}
|
|
const curlSrc = await readFile(
|
|
path.join(__dirname, '../../kernel/bin/curl'),
|
|
'utf8'
|
|
)
|
|
await sys.put('/bin/curl', b4a.from(curlSrc))
|
|
await runBinCommand(ctx, ['curl', 'http://example.test/x'])
|
|
t.ok(ran)
|
|
await store.close()
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
async function readBuiltBin(name) {
|
|
const fs = await import('node:fs/promises')
|
|
const p = path.join(__dirname, '../../kernel/bin', name)
|
|
return fs.readFile(p, 'utf8')
|
|
}
|