167 lines
5.2 KiB
JavaScript
167 lines
5.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,
|
|
bareOsQvacMaterializeGgufToHost,
|
|
bareOsQvacMirrorHostGgufToHdms,
|
|
bareOsQvacListHdmsGgufs,
|
|
bareOsQvacFindHdmsGguf,
|
|
BARE_OS_QVAC_MODELS_GUEST_ROOT
|
|
} from './lib/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 })
|
|
}
|
|
})
|