import { readFile } from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import test from 'brittle' import b4a from 'b4a' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor async function loadAppstoreBin() { const runtime = await readFile(path.join(__dirname, '../lib/runtime.js'), 'utf8') const p2p = await readFile(path.join(__dirname, '../lib/p2p/p2p-suite.js'), 'utf8') const pear = await readFile(path.join(__dirname, '../lib/pear/appstore-pear.js'), 'utf8') const body = await readFile(path.join(__dirname, '../src/appstore.js'), 'utf8') return new AsyncFunction( 'ctx', 'argv', `${runtime}\n${p2p}\n${pear}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n` ) } function mkMemVfs(initial = {}) { /** @type {Map} */ const files = new Map(Object.entries(initial)) return { files, async mkdir() {}, async readdir(p) { const prefix = p.endsWith('/') ? p : p + '/' const names = new Set() for (const key of files.keys()) { if (!key.startsWith(prefix)) continue names.add(key.slice(prefix.length).split('/')[0]) } return [...names].filter(Boolean) }, async stat(p) { if (!files.has(p)) { for (const key of files.keys()) { if (key.startsWith(p + '/')) return { isDirectory: true, type: 'directory' } } throw new Error('ENOENT') } return { isDirectory: false, type: 'file' } }, async readFile(p) { if (!files.has(p)) throw new Error('ENOENT ' + p) return files.get(p) }, async writeFile(p, buf) { files.set(p, buf instanceof Uint8Array ? buf : new Uint8Array(buf)) } } } test('appstore install fetches pear tree from local HDMS mount', async (t) => { const key = '8wdec11bcygidig1qubkuexgfjw57we5whgcsbsajao73g9eagry' const mount = '/mnt/pear-my-pear-app' const home = '/home/guest' const vfs = mkMemVfs({ [`${mount}/sources/index.js`]: b4a.from('console.log("Hello from my Pear app!");\n', 'utf8'), [`${mount}/package.json`]: b4a.from( JSON.stringify({ name: 'my-pear-app', main: 'index.js', version: '0.1.0' }), 'utf8' ) }) const hdms = { active: true, byLabel: new Map([ [ 'pear-my-pear-app', { drive: {}, entry: { label: 'pear-my-pear-app', key, mode: 'writable' } } ] ]), registry: { version: 1, drives: [{ label: 'pear-my-pear-app', key, mode: 'writable' }] } } const logs = [] const ctx = { vfs, b4a, env: { HOME: home }, identity: { state: 'unlocked' }, disk: { hdmsController: hdms }, console: { log(...a) { logs.push(a.join(' ')) }, error() {}, warn() {} } } const run = await loadAppstoreBin() await run(ctx, [ 'appstore', 'install', 'my-pear-app', `pear://0.10.${key}`, '--yes' ]) const pkgDir = `${home}/.appstore/packages/my-pear-app` t.ok(vfs.files.has(`${pkgDir}/sources/index.js`)) t.ok(vfs.files.has(`${pkgDir}/manifest.json`)) t.ok(logs.some((l) => l.includes('fetched: 2 file'))) }) test('appstore launch runs materialized entry script', async (t) => { const home = '/home/guest' const pkgDir = `${home}/.appstore/packages/my-pear-app` const vfs = mkMemVfs({ [`${home}/.appstore/registry.json`]: b4a.from( JSON.stringify({ schema: 1, packages: [ { name: 'my-pear-app', pearLink: 'pear://abc', type: 'app' } ] }), 'utf8' ), [`${pkgDir}/sources/index.js`]: b4a.from('console.log("Hello from my Pear app!");\n', 'utf8'), [`${pkgDir}/package.json`]: b4a.from(JSON.stringify({ main: 'index.js' }), 'utf8') }) const logs = [] const ctx = { vfs, b4a, env: { HOME: home }, console: { log(...a) { logs.push(a.join(' ')) }, error() {}, warn() {} } } const run = await loadAppstoreBin() await run(ctx, ['appstore', 'launch', 'my-pear-app']) t.ok(logs.some((l) => l.includes('Hello from my Pear app!'))) t.ok(logs.some((l) => l.includes('Launched "my-pear-app"'))) })