import test from 'brittle'
import {
buildCatalogRequest,
parseAllowlist,
isValidHexKey,
resolveApiUrl,
resolveApiKey,
formatPending,
runCatalogCommand
} from 'p2p-hiverelay/cli/catalog.js'
import {
buildFederationRequest,
isValidUrl,
formatFederationList,
runFederationCommand
} from 'p2p-hiverelay/cli/federation.js'
import {
buildQvacRequest,
formatQvacModelList,
runQvacCommand
} from 'p2p-hiverelay/cli/qvac.js'
const HEX64 = 'a'.repeat(64)
const HEX64_B = 'b'.repeat(64)
// ─── helpers ────────────────────────────────────────────────────────
function makeFakeFetch (response) {
const calls = []
const fetchImpl = async (url, init) => {
calls.push({ url, init })
const r = typeof response === 'function' ? response({ url, init }) : response
return {
ok: r.ok !== false,
status: r.status || 200,
statusText: r.statusText || 'OK',
text: async () => r.text === undefined ? JSON.stringify(r.body || {}) : r.text
}
}
return { fetchImpl, calls }
}
function captureStreams () {
const out = []
const err = []
return {
out: (msg) => out.push(String(msg)),
err: (msg) => err.push(String(msg)),
outLines: out,
errLines: err
}
}
// ─── pure helpers ───────────────────────────────────────────────────
test('catalog: isValidHexKey accepts 64 hex chars and rejects others', (t) => {
t.is(isValidHexKey(HEX64), true)
t.is(isValidHexKey(HEX64.toUpperCase()), true)
t.is(isValidHexKey('xyz'), false)
t.is(isValidHexKey('a'.repeat(63)), false)
t.is(isValidHexKey('a'.repeat(65)), false)
t.is(isValidHexKey(''), false)
t.is(isValidHexKey(null), false)
})
test('catalog: parseAllowlist splits comma-separated values and trims', (t) => {
t.alike(parseAllowlist('a,b,c'), ['a', 'b', 'c'])
t.alike(parseAllowlist(' a , b , c '), ['a', 'b', 'c'])
t.alike(parseAllowlist(''), [])
t.alike(parseAllowlist(undefined), [])
t.alike(parseAllowlist(['a,b', 'c']), ['a', 'b', 'c'])
})
test('catalog: resolveApiUrl defaults to http://127.0.0.1:9100', (t) => {
t.is(resolveApiUrl({}), 'http://127.0.0.1:9100')
})
test('catalog: resolveApiUrl normalizes and strips trailing slash', (t) => {
t.is(resolveApiUrl({ 'api-url': 'http://example.com:9100/' }), 'http://example.com:9100')
t.is(resolveApiUrl({ 'api-url': 'https://relay.example' }), 'https://relay.example')
})
test('catalog: resolveApiUrl rejects URLs missing http(s)://', (t) => {
t.exception(() => resolveApiUrl({ 'api-url': 'example.com' }), /must start with http/)
t.exception(() => resolveApiUrl({ 'api-url': 'ftp://x' }), /must start with http/)
})
test('catalog: resolveApiKey prefers --api-key flag, then env', (t) => {
t.is(resolveApiKey({ 'api-key': 'flagkey' }, { HIVERELAY_API_KEY: 'envkey' }), 'flagkey')
t.is(resolveApiKey({}, { HIVERELAY_API_KEY: 'envkey' }), 'envkey')
t.is(resolveApiKey({}, {}), null)
})
// ─── catalog request builder ────────────────────────────────────────
test('catalog: buildCatalogRequest mode — valid modes', (t) => {
for (const m of ['open', 'review', 'allowlist', 'closed']) {
const r = buildCatalogRequest('mode', [m], {})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/catalog/mode')
t.alike(r.body, { mode: m })
}
})
test('catalog: buildCatalogRequest mode — invalid mode throws', (t) => {
t.exception(() => buildCatalogRequest('mode', ['nope'], {}), /one of/)
t.exception(() => buildCatalogRequest('mode', [], {}), /one of/)
})
test('catalog: buildCatalogRequest allowlist — valid pubkeys', (t) => {
const r = buildCatalogRequest('allowlist', [HEX64 + ',' + HEX64_B], {})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/catalog/allowlist')
t.alike(r.body, { allowlist: [HEX64, HEX64_B] })
})
test('catalog: buildCatalogRequest allowlist — empty list throws', (t) => {
t.exception(() => buildCatalogRequest('allowlist', [''], {}), /at least one pubkey/)
t.exception(() => buildCatalogRequest('allowlist', [], {}), /at least one pubkey/)
})
test('catalog: buildCatalogRequest allowlist — non-hex entry throws', (t) => {
t.exception(() => buildCatalogRequest('allowlist', ['xyz'], {}), /64 hex/)
t.exception(() => buildCatalogRequest('allowlist', [HEX64 + ',xyz'], {}), /64 hex/)
})
test('catalog: buildCatalogRequest approve/reject/remove — valid appKey', (t) => {
for (const sub of ['approve', 'reject', 'remove']) {
const r = buildCatalogRequest(sub, [HEX64], {})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/catalog/' + sub)
t.alike(r.body, { appKey: HEX64 })
}
})
test('catalog: buildCatalogRequest approve/reject/remove — invalid appKey throws', (t) => {
for (const sub of ['approve', 'reject', 'remove']) {
t.exception(() => buildCatalogRequest(sub, ['notHex'], {}), /64 hex/)
t.exception(() => buildCatalogRequest(sub, [], {}), /64 hex/)
}
})
test('catalog: buildCatalogRequest pending — GET, no body', (t) => {
const r = buildCatalogRequest('pending', [], {})
t.is(r.method, 'GET')
t.is(r.path, '/api/manage/catalog/pending')
t.is(r.body, null)
})
test('catalog: buildCatalogRequest unknown subcommand throws', (t) => {
t.exception(() => buildCatalogRequest('frobnicate', [], {}), /Unknown catalog/)
})
// ─── catalog dispatcher (request shape + auth) ──────────────────────
test('catalog: runCatalogCommand mode — sends POST with bearer auth', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true, mode: 'review' } })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: { 'api-url': 'http://relay.test', 'api-key': 'secret' },
positional: ['mode', 'review'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
t.is(calls.length, 1)
t.is(calls[0].url, 'http://relay.test/api/manage/catalog/mode')
t.is(calls[0].init.method, 'POST')
t.is(calls[0].init.headers.Authorization, 'Bearer secret')
t.is(calls[0].init.headers['Content-Type'], 'application/json')
t.alike(JSON.parse(calls[0].init.body), { mode: 'review' })
t.ok(streams.outLines[0].includes('OK'))
})
test('catalog: runCatalogCommand reads HIVERELAY_API_KEY from env', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true } })
const streams = captureStreams()
await runCatalogCommand({
argv: {},
positional: ['approve', HEX64],
env: { HIVERELAY_API_KEY: 'envsecret' },
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(calls[0].init.headers.Authorization, 'Bearer envsecret')
t.is(calls[0].url, 'http://127.0.0.1:9100/api/manage/catalog/approve')
})
test('catalog: runCatalogCommand pending — GET, pretty-prints', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({
body: {
count: 1,
mode: 'review',
requests: [{
appKey: HEX64,
publisherPubkey: HEX64_B,
privacyTier: 'public',
discoveredAt: 1700000000000
}]
}
})
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {}, positional: ['pending'], env: {}, fetchImpl, out: streams.out, err: streams.err
})
t.is(code, 0)
t.is(calls[0].init.method, 'GET')
t.absent(calls[0].init.body)
const printed = streams.outLines.join('\n')
t.ok(printed.includes('Pending requests: 1'))
t.ok(printed.includes('mode: review'))
t.ok(printed.includes(HEX64.slice(0, 16)))
})
test('catalog: runCatalogCommand pending — empty queue', async (t) => {
const { fetchImpl } = makeFakeFetch({ body: { count: 0, mode: 'open', requests: [] } })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {}, positional: ['pending'], env: {}, fetchImpl, out: streams.out, err: streams.err
})
t.is(code, 0)
t.ok(streams.outLines[0].includes('No pending'))
})
test('catalog: runCatalogCommand returns exit code 1 on validation failure', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: {} })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {}, positional: ['mode', 'invalid'], env: {}, fetchImpl, out: streams.out, err: streams.err
})
t.is(code, 1)
t.is(calls.length, 0, 'no HTTP call made on validation failure')
t.ok(streams.errLines.length > 0)
})
test('catalog: runCatalogCommand returns exit code 1 on bad --api-url', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: {} })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: { 'api-url': 'no-scheme' },
positional: ['mode', 'open'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 1)
t.is(calls.length, 0)
})
test('catalog: runCatalogCommand returns exit code 1 on HTTP error', async (t) => {
const { fetchImpl } = makeFakeFetch({ ok: false, status: 401, statusText: 'Unauthorized', body: { error: 'API key required' } })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {}, positional: ['approve', HEX64], env: {}, fetchImpl, out: streams.out, err: streams.err
})
t.is(code, 1)
t.ok(streams.errLines[0].includes('API key required'))
})
test('catalog: runCatalogCommand allowlist sends array of pubkeys', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true, allowlist: [HEX64, HEX64_B] } })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {},
positional: ['allowlist', HEX64 + ',' + HEX64_B],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
const sent = JSON.parse(calls[0].init.body)
t.alike(sent, { allowlist: [HEX64, HEX64_B] })
})
test('catalog: runCatalogCommand allowlist invalid hex returns exit 1', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: {} })
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {},
positional: ['allowlist', 'not-hex'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 1)
t.is(calls.length, 0)
})
test('catalog: formatPending handles missing fields gracefully', (t) => {
t.is(formatPending(null), 'No pending catalog requests (mode: unknown).')
t.is(formatPending({ requests: [] }), 'No pending catalog requests (mode: unknown).')
const printed = formatPending({
count: 1,
mode: 'review',
requests: [{ appKey: HEX64 }]
})
t.ok(printed.includes('Pending requests: 1'))
t.ok(printed.includes('publisher:unknown'))
})
test('catalog: runCatalogCommand help — prints help, exit 0', async (t) => {
const streams = captureStreams()
const code = await runCatalogCommand({
argv: {}, positional: [], env: {}, out: streams.out, err: streams.err
})
t.is(code, 0)
t.ok(streams.outLines[0].includes('Usage:'))
})
test('catalog: runCatalogCommand without api-key sends no Authorization header', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true } })
const streams = captureStreams()
await runCatalogCommand({
argv: {}, positional: ['approve', HEX64], env: {}, fetchImpl, out: streams.out, err: streams.err
})
t.absent(calls[0].init.headers.Authorization)
})
// ─── federation pure helpers ────────────────────────────────────────
test('federation: isValidUrl accepts http/https only', (t) => {
t.is(isValidUrl('http://x.com'), true)
t.is(isValidUrl('https://x.com'), true)
t.is(isValidUrl(' http://x.com '), true)
t.is(isValidUrl('ftp://x.com'), false)
t.is(isValidUrl('x.com'), false)
t.is(isValidUrl(''), false)
t.is(isValidUrl(null), false)
})
// ─── federation request builder ─────────────────────────────────────
test('federation: buildFederationRequest list — GET', (t) => {
const r = buildFederationRequest('list', [], {})
t.is(r.method, 'GET')
t.is(r.path, '/api/manage/federation')
t.is(r.body, null)
})
test('federation: buildFederationRequest follow — valid url', (t) => {
const r = buildFederationRequest('follow', ['https://relay.example'], {})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/federation/follow')
t.alike(r.body, { url: 'https://relay.example' })
})
test('federation: buildFederationRequest follow — with --pubkey', (t) => {
const r = buildFederationRequest('follow', ['https://relay.example'], { pubkey: HEX64 })
t.alike(r.body, { url: 'https://relay.example', pubkey: HEX64 })
})
test('federation: buildFederationRequest follow — invalid url throws', (t) => {
t.exception(() => buildFederationRequest('follow', ['relay.example'], {}), /http/)
t.exception(() => buildFederationRequest('follow', [], {}), /http/)
})
test('federation: buildFederationRequest follow — invalid pubkey throws', (t) => {
t.exception(
() => buildFederationRequest('follow', ['http://x'], { pubkey: 'short' }),
/64 hex/
)
})
test('federation: buildFederationRequest mirror — POSTs to mirror endpoint', (t) => {
const r = buildFederationRequest('mirror', ['http://x'], {})
t.is(r.path, '/api/manage/federation/mirror')
})
test('federation: buildFederationRequest unfollow — POSTs url', (t) => {
const r = buildFederationRequest('unfollow', ['http://x'], {})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/federation/unfollow')
t.alike(r.body, { url: 'http://x' })
})
test('federation: buildFederationRequest republish — minimal valid', (t) => {
const r = buildFederationRequest('republish', [HEX64], { source: 'http://upstream' })
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/federation/republish')
t.alike(r.body, { appKey: HEX64, sourceUrl: 'http://upstream' })
})
test('federation: buildFederationRequest republish — with all options', (t) => {
const r = buildFederationRequest('republish', [HEX64], {
source: 'http://upstream',
pubkey: HEX64_B,
channel: 'partners',
note: 'curated'
})
t.alike(r.body, {
appKey: HEX64,
sourceUrl: 'http://upstream',
sourcePubkey: HEX64_B,
channel: 'partners',
note: 'curated'
})
})
test('federation: buildFederationRequest republish — invalid appKey throws', (t) => {
t.exception(
() => buildFederationRequest('republish', ['x'], { source: 'http://x' }),
/64 hex/
)
})
test('federation: buildFederationRequest republish — missing/invalid --source throws', (t) => {
t.exception(
() => buildFederationRequest('republish', [HEX64], {}),
/--source/
)
t.exception(
() => buildFederationRequest('republish', [HEX64], { source: 'no-scheme' }),
/--source/
)
})
test('federation: buildFederationRequest republish — invalid pubkey throws', (t) => {
t.exception(
() => buildFederationRequest('republish', [HEX64], { source: 'http://x', pubkey: 'short' }),
/64 hex/
)
})
test('federation: buildFederationRequest unrepublish — appKey body', (t) => {
const r = buildFederationRequest('unrepublish', [HEX64], {})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/federation/unrepublish')
t.alike(r.body, { appKey: HEX64 })
})
test('federation: buildFederationRequest unrepublish — invalid appKey throws', (t) => {
t.exception(() => buildFederationRequest('unrepublish', ['x'], {}), /64 hex/)
})
test('federation: buildFederationRequest unknown subcommand throws', (t) => {
t.exception(() => buildFederationRequest('frobnicate', [], {}), /Unknown federation/)
})
// ─── federation dispatcher ──────────────────────────────────────────
test('federation: runFederationCommand list — GET, pretty-prints', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({
body: {
subscriptions: [{ url: 'http://up.example', mode: 'follow', pubkey: HEX64 }],
republishes: [{ appKey: HEX64_B, sourceUrl: 'http://up.example', channel: 'main' }]
}
})
const streams = captureStreams()
const code = await runFederationCommand({
argv: {}, positional: ['list'], env: {}, fetchImpl, out: streams.out, err: streams.err
})
t.is(code, 0)
t.is(calls[0].init.method, 'GET')
t.is(calls[0].url, 'http://127.0.0.1:9100/api/manage/federation')
const printed = streams.outLines.join('\n')
t.ok(printed.includes('Federation subscriptions: 1'))
t.ok(printed.includes('Republishes: 1'))
t.ok(printed.includes('http://up.example'))
})
test('federation: runFederationCommand follow — POST with bearer auth', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true } })
const streams = captureStreams()
const code = await runFederationCommand({
argv: { 'api-key': 'secret' },
positional: ['follow', 'http://relay.example'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
t.is(calls[0].init.method, 'POST')
t.is(calls[0].url, 'http://127.0.0.1:9100/api/manage/federation/follow')
t.is(calls[0].init.headers.Authorization, 'Bearer secret')
t.alike(JSON.parse(calls[0].init.body), { url: 'http://relay.example' })
})
test('federation: runFederationCommand republish — sends sourceUrl/channel/note', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true } })
const streams = captureStreams()
const code = await runFederationCommand({
argv: { source: 'http://upstream', channel: 'partners', note: 'curated' },
positional: ['republish', HEX64],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
const sent = JSON.parse(calls[0].init.body)
t.alike(sent, {
appKey: HEX64,
sourceUrl: 'http://upstream',
channel: 'partners',
note: 'curated'
})
})
test('federation: runFederationCommand exit 1 on bad URL', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: {} })
const streams = captureStreams()
const code = await runFederationCommand({
argv: {},
positional: ['follow', 'not-a-url'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 1)
t.is(calls.length, 0)
})
test('federation: runFederationCommand exit 1 on missing --source for republish', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: {} })
const streams = captureStreams()
const code = await runFederationCommand({
argv: {},
positional: ['republish', HEX64],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 1)
t.is(calls.length, 0)
})
test('federation: runFederationCommand exit 1 on HTTP error', async (t) => {
const { fetchImpl } = makeFakeFetch({ ok: false, status: 503, statusText: 'Service Unavailable', body: { error: 'Federation not initialized' } })
const streams = captureStreams()
const code = await runFederationCommand({
argv: {},
positional: ['follow', 'http://x'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 1)
t.ok(streams.errLines[0].includes('Federation not initialized'))
})
test('federation: runFederationCommand help — prints help, exit 0', async (t) => {
const streams = captureStreams()
const code = await runFederationCommand({
argv: {}, positional: [], env: {}, out: streams.out, err: streams.err
})
t.is(code, 0)
t.ok(streams.outLines[0].includes('Usage:'))
})
test('federation: formatFederationList handles empty payload', (t) => {
const printed = formatFederationList({})
t.ok(printed.includes('Federation subscriptions: 0'))
t.ok(printed.includes('Republishes: 0'))
})
test('federation: runFederationCommand custom --api-url is used', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true } })
const streams = captureStreams()
await runFederationCommand({
argv: { 'api-url': 'https://custom.example:8443' },
positional: ['list'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(calls[0].url, 'https://custom.example:8443/api/manage/federation')
})
test('federation: runFederationCommand mirror sends to mirror path', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({ body: { ok: true } })
const streams = captureStreams()
await runFederationCommand({
argv: {},
positional: ['mirror', 'http://x'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(calls[0].url, 'http://127.0.0.1:9100/api/manage/federation/mirror')
t.alike(JSON.parse(calls[0].init.body), { url: 'http://x' })
})
test('federation: runFederationCommand unrepublish — exit 0 even when removed=false', async (t) => {
const { fetchImpl } = makeFakeFetch({ body: { ok: true, removed: false, appKey: HEX64 } })
const streams = captureStreams()
const code = await runFederationCommand({
argv: {},
positional: ['unrepublish', HEX64],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
t.ok(streams.outLines[0].includes('no-op'))
})
// ─── qvac request builder + dispatcher ─────────────────────────────
test('qvac: buildQvacRequest register builds manage API payload', (t) => {
const r = buildQvacRequest('register', ['local-qvac'], {
type: 'llm',
'model-src': '/models/tiny.gguf',
'model-type': 'llm',
'model-config': '{"ctx_size":256}',
'completion-options': '{"temperature":0}',
'fallback-http': false
})
t.is(r.method, 'POST')
t.is(r.path, '/api/manage/ai/models')
t.is(r.body.backend, 'qvac')
t.is(r.body.modelId, 'local-qvac')
t.is(r.body.modelSrc, '/models/tiny.gguf')
t.alike(r.body.modelConfig, { ctx_size: 256 })
t.alike(r.body.qvac.completionOptions, { temperature: 0 })
t.is(r.body.qvac.fallbackToHttp, false)
})
test('qvac: buildQvacRequest register requires qvac source or loaded id', (t) => {
t.exception(() => buildQvacRequest('register', ['local-qvac'], {}), /model-src/)
})
test('qvac: buildQvacRequest list/remove aliases', (t) => {
t.alike(buildQvacRequest('ls', [], {}), {
method: 'GET',
path: '/api/manage/ai/models',
body: null
})
const remove = buildQvacRequest('rm', ['local-qvac'], {})
t.is(remove.method, 'POST')
t.is(remove.path, '/api/manage/ai/models/remove')
t.alike(remove.body, { modelId: 'local-qvac' })
})
test('qvac: formatQvacModelList surfaces model status', (t) => {
const printed = formatQvacModelList({
qvac: { enabled: true, checked: true, available: true, module: '@qvac/sdk' },
models: [{
modelId: 'local-qvac',
type: 'llm',
backend: 'qvac',
status: 'loaded',
qvac: { loaded: true, delegated: false, hasModelSrc: true },
stats: { requests: 2, errors: 0 }
}]
})
t.ok(printed.includes('QVAC SDK: available'))
t.ok(printed.includes('local-qvac'))
t.ok(printed.includes('status:loaded'))
t.ok(printed.includes('requests:2'))
})
test('qvac: runQvacCommand list sends bearer auth and pretty prints', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({
body: {
ok: true,
qvac: { enabled: true, checked: true, available: true, module: '@qvac/sdk' },
qvacCount: 1,
models: [{
modelId: 'local-qvac',
type: 'llm',
backend: 'qvac',
status: 'registered',
qvac: { loaded: false, delegated: false, hasModelSrc: true },
stats: { requests: 0, errors: 0 }
}]
}
})
const streams = captureStreams()
const code = await runQvacCommand({
argv: { 'api-url': 'http://relay.test', 'api-key': 'secret' },
positional: ['list'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
t.is(calls[0].url, 'http://relay.test/api/manage/ai/models')
t.is(calls[0].init.method, 'GET')
t.is(calls[0].init.headers.Authorization, 'Bearer secret')
t.ok(streams.outLines.join('\n').includes('status:registered'))
})
test('qvac: runQvacCommand register posts to manage API', async (t) => {
const { fetchImpl, calls } = makeFakeFetch({
body: {
ok: true,
model: {
modelId: 'local-qvac',
type: 'llm',
backend: 'qvac',
status: 'registered',
qvac: { loaded: false }
}
}
})
const streams = captureStreams()
const code = await runQvacCommand({
argv: { 'model-src': '/models/tiny.gguf' },
positional: ['register', 'local-qvac'],
env: {},
fetchImpl,
out: streams.out,
err: streams.err
})
t.is(code, 0)
t.is(calls[0].url, 'http://127.0.0.1:9100/api/manage/ai/models')
t.is(calls[0].init.method, 'POST')
t.alike(JSON.parse(calls[0].init.body), {
modelId: 'local-qvac',
type: 'llm',
backend: 'qvac',
modelSrc: '/models/tiny.gguf'
})
t.ok(streams.outLines[0].includes('status:registered'))
})