235 lines
8.2 KiB
JavaScript
235 lines
8.2 KiB
JavaScript
import test from 'brittle'
|
|
import fs from 'fs'
|
|
import os from 'os'
|
|
import path from 'path'
|
|
import {
|
|
bareOsQvacModelsGuestPath,
|
|
bareOsQvacModelsHostPath,
|
|
bareOsQvacModelsStripHashPrefix,
|
|
bareOsQvacHostGgufMatchesSize,
|
|
bareOsQvacPrepareHostCacheConfig,
|
|
bareOsQvacIsHostJsonConfigPath,
|
|
bareOsQvacTryReadHostJsonConfig,
|
|
bareOsQvacMaterializeGgufToHost,
|
|
bareOsQvacMirrorHostGgufToHdms,
|
|
bareOsQvacListHdmsGgufs,
|
|
bareOsQvacFindHdmsGguf,
|
|
BARE_OS_QVAC_MODELS_GUEST_ROOT
|
|
} from './lib/services/bare-os-qvac-models-store.mjs'
|
|
|
|
test('guest/host path + hash prefix mapping', async (t) => {
|
|
t.is(
|
|
bareOsQvacModelsGuestPath('5b8aae816570a09d_Qwen3-0.6B-Q4_0.gguf'),
|
|
BARE_OS_QVAC_MODELS_GUEST_ROOT + '/5b8aae816570a09d_Qwen3-0.6B-Q4_0.gguf'
|
|
)
|
|
t.is(
|
|
bareOsQvacModelsStripHashPrefix('5b8aae816570a09d_Qwen3-0.6B-Q4_0.gguf'),
|
|
'Qwen3-0.6B-Q4_0.gguf'
|
|
)
|
|
t.is(
|
|
bareOsQvacModelsStripHashPrefix('Qwen3-0.6B-Q4_0.gguf'),
|
|
'Qwen3-0.6B-Q4_0.gguf'
|
|
)
|
|
const host = bareOsQvacModelsHostPath('a.gguf', '/tmp/qvac-cache')
|
|
t.is(host, path.join('/tmp/qvac-cache', 'a.gguf'))
|
|
})
|
|
|
|
test('skip materialize when host size matches', async (t) => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bare-os-qvac-models-'))
|
|
const hostPath = path.join(dir, 'model.gguf')
|
|
const payload = Buffer.from('GGUF-fake-payload-0123456789')
|
|
fs.writeFileSync(hostPath, payload)
|
|
t.ok(bareOsQvacHostGgufMatchesSize(hostPath, payload.byteLength))
|
|
t.absent(bareOsQvacHostGgufMatchesSize(hostPath, payload.byteLength + 1))
|
|
|
|
/** @type {Record<string, Buffer>} */
|
|
const files = {
|
|
[BARE_OS_QVAC_MODELS_GUEST_ROOT + '/model.gguf']: payload
|
|
}
|
|
const vfs = {
|
|
async stat(p) {
|
|
const b = files[p]
|
|
if (!b) throw new Error('ENOENT')
|
|
return { size: b.byteLength }
|
|
},
|
|
async readFile(p) {
|
|
const b = files[p]
|
|
if (!b) throw new Error('ENOENT')
|
|
return b
|
|
},
|
|
async writeFile() {
|
|
throw new Error('should not write')
|
|
},
|
|
async readdir() {
|
|
return ['model.gguf']
|
|
}
|
|
}
|
|
|
|
const r = await bareOsQvacMaterializeGgufToHost({
|
|
vfs,
|
|
guestPath: BARE_OS_QVAC_MODELS_GUEST_ROOT + '/model.gguf',
|
|
hostPath,
|
|
guestSize: payload.byteLength
|
|
})
|
|
t.ok(r.ok)
|
|
t.ok(r.skipped)
|
|
t.is(r.reason, 'size_match')
|
|
|
|
fs.rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('materialize + mirror round-trip via fake vfs', async (t) => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bare-os-qvac-models-'))
|
|
const hostPath = path.join(dir, 'hash_demo.gguf')
|
|
const payload = Buffer.from('GGUF-roundtrip-bytes')
|
|
/** @type {Record<string, Buffer>} */
|
|
const files = {
|
|
[BARE_OS_QVAC_MODELS_GUEST_ROOT + '/hash_demo.gguf']: payload
|
|
}
|
|
const vfs = {
|
|
async stat(p) {
|
|
const b = files[p]
|
|
if (!b) throw new Error('ENOENT')
|
|
return { size: b.byteLength }
|
|
},
|
|
async readFile(p) {
|
|
const b = files[p]
|
|
if (!b) throw new Error('ENOENT')
|
|
return b
|
|
},
|
|
async writeFile(p, buf) {
|
|
files[p] = Buffer.from(buf)
|
|
},
|
|
async readdir() {
|
|
return Object.keys(files).map((p) => path.basename(p))
|
|
}
|
|
}
|
|
|
|
const mat = await bareOsQvacMaterializeGgufToHost({
|
|
vfs,
|
|
guestPath: BARE_OS_QVAC_MODELS_GUEST_ROOT + '/hash_demo.gguf',
|
|
hostPath
|
|
})
|
|
t.ok(mat.ok)
|
|
t.absent(mat.skipped)
|
|
t.is(fs.readFileSync(hostPath).equals(payload), true)
|
|
|
|
// Change host and mirror back
|
|
const next = Buffer.from('GGUF-roundtrip-bytes-UPDATED')
|
|
fs.writeFileSync(hostPath, next)
|
|
const mir = await bareOsQvacMirrorHostGgufToHdms({
|
|
vfs,
|
|
hostPath,
|
|
guestName: 'hash_demo.gguf'
|
|
})
|
|
t.ok(mir.ok)
|
|
t.absent(mir.skipped)
|
|
t.ok(
|
|
files[BARE_OS_QVAC_MODELS_GUEST_ROOT + '/hash_demo.gguf'].equals(next)
|
|
)
|
|
|
|
const names = await bareOsQvacListHdmsGgufs(vfs)
|
|
t.ok(names.includes('hash_demo.gguf'))
|
|
const found = await bareOsQvacFindHdmsGguf(vfs, 'hash_demo.gguf')
|
|
t.is(found, BARE_OS_QVAC_MODELS_GUEST_ROOT + '/hash_demo.gguf')
|
|
|
|
fs.rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
test('prepareHostCacheConfig writes absolute cacheDirectory', async (t) => {
|
|
const prev = process.env.BARE_OS_HOST_DATA
|
|
const prevCfg = process.env.QVAC_CONFIG_PATH
|
|
const prevRequire = globalThis.require
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'bare-os-host-data-'))
|
|
process.env.BARE_OS_HOST_DATA = root
|
|
try {
|
|
delete globalThis.require
|
|
const cacheDir = path.join(root, 'qvac', 'models')
|
|
const prepared = bareOsQvacPrepareHostCacheConfig({ cacheDir })
|
|
t.is(prepared.cacheDir, cacheDir)
|
|
t.ok(fs.existsSync(prepared.configPath))
|
|
const body = JSON.parse(fs.readFileSync(prepared.configPath, 'utf8'))
|
|
t.is(body.cacheDirectory, cacheDir)
|
|
t.is(body.loggerLevel, 'error')
|
|
t.is(body.loggerConsoleOutput, true)
|
|
t.is(process.env.QVAC_CONFIG_PATH, prepared.configPath)
|
|
t.is(typeof globalThis.require, 'function')
|
|
const viaRequire = globalThis.require(prepared.configPath)
|
|
t.is(viaRequire.cacheDirectory, cacheDir)
|
|
} finally {
|
|
if (prevRequire === undefined) delete globalThis.require
|
|
else globalThis.require = prevRequire
|
|
if (prev === undefined) delete process.env.BARE_OS_HOST_DATA
|
|
else process.env.BARE_OS_HOST_DATA = prev
|
|
if (prevCfg === undefined) delete process.env.QVAC_CONFIG_PATH
|
|
else process.env.QVAC_CONFIG_PATH = prevCfg
|
|
fs.rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('config require shim wraps existing Bare require for host JSON', async (t) => {
|
|
const prev = process.env.BARE_OS_HOST_DATA
|
|
const prevCfg = process.env.QVAC_CONFIG_PATH
|
|
const prevRequire = globalThis.require
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'bare-os-host-data-'))
|
|
process.env.BARE_OS_HOST_DATA = root
|
|
/** @type {string[]} */
|
|
const delegated = []
|
|
function existingRequire(id) {
|
|
delegated.push(String(id))
|
|
if (id === 'events') return { EventEmitter: function EventEmitter() {} }
|
|
const err = new Error(
|
|
"MODULE_NOT_FOUND: Cannot find module '" +
|
|
id +
|
|
"' imported from 'bare:/app.bundle/packages/bare-os-booter/lib/ctx/bare-os-ctx-bare.js'"
|
|
)
|
|
err.code = 'MODULE_NOT_FOUND'
|
|
throw err
|
|
}
|
|
existingRequire.addon = function addon() {
|
|
return {}
|
|
}
|
|
existingRequire.resolve = function resolve() {
|
|
return 'ok'
|
|
}
|
|
try {
|
|
globalThis.require = existingRequire
|
|
const cacheDir = path.join(root, 'qvac', 'models')
|
|
const prepared = bareOsQvacPrepareHostCacheConfig({ cacheDir })
|
|
t.ok(globalThis.require.__bareOsQvacConfigRequire)
|
|
t.is(typeof globalThis.require.addon, 'function')
|
|
t.is(globalThis.require.resolve(), 'ok')
|
|
const viaRequire = globalThis.require(prepared.configPath)
|
|
t.is(viaRequire.cacheDirectory, cacheDir)
|
|
t.ok(!delegated.includes(prepared.configPath))
|
|
const ev = globalThis.require('events')
|
|
t.is(typeof ev.EventEmitter, 'function')
|
|
t.ok(delegated.includes('events'))
|
|
} finally {
|
|
if (prevRequire === undefined) delete globalThis.require
|
|
else globalThis.require = prevRequire
|
|
if (prev === undefined) delete process.env.BARE_OS_HOST_DATA
|
|
else process.env.BARE_OS_HOST_DATA = prev
|
|
if (prevCfg === undefined) delete process.env.QVAC_CONFIG_PATH
|
|
else process.env.QVAC_CONFIG_PATH = prevCfg
|
|
fs.rmSync(root, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('host JSON config path accepts POSIX and Windows, rejects URLs', async (t) => {
|
|
t.ok(bareOsQvacIsHostJsonConfigPath('/home/raven/.bare-os/qvac/qvac.config.json'))
|
|
t.ok(bareOsQvacIsHostJsonConfigPath('C:\\Users\\raven\\.bare-os\\qvac\\qvac.config.json'))
|
|
t.ok(bareOsQvacIsHostJsonConfigPath('C:/Users/raven/.bare-os/qvac/qvac.config.json'))
|
|
t.absent(bareOsQvacIsHostJsonConfigPath('bare:/home/raven/.bare-os/qvac/qvac.config.json'))
|
|
t.absent(bareOsQvacIsHostJsonConfigPath('file:///home/raven/.bare-os/qvac/qvac.config.json'))
|
|
t.absent(bareOsQvacIsHostJsonConfigPath('qvac.config.js'))
|
|
t.absent(bareOsQvacIsHostJsonConfigPath('relative/qvac.config.json'))
|
|
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'bare-os-qvac-cfg-'))
|
|
const cfg = path.join(dir, 'qvac.config.json')
|
|
fs.writeFileSync(cfg, JSON.stringify({ cacheDirectory: '/tmp/models' }))
|
|
t.is(bareOsQvacTryReadHostJsonConfig(cfg).cacheDirectory, '/tmp/models')
|
|
t.is(bareOsQvacTryReadHostJsonConfig('bare:' + cfg), undefined)
|
|
fs.rmSync(dir, { recursive: true, force: true })
|
|
})
|