++
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { mkdtemp, mkdir, rm, writeFile } from 'fs/promises'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { performance } from 'perf_hooks'
|
||||
|
||||
vi.mock('../../packages/server/src/services/hermes/hermes-process', () => ({
|
||||
execHermesWithBin: vi.fn(() => { throw new Error('Inventory must not invoke CLI') }),
|
||||
spawnHermesWithBin: vi.fn(),
|
||||
}))
|
||||
import { listProfilesSnapshot } from '../../packages/server/src/services/hermes/hermes-cli'
|
||||
import { execHermesWithBin } from '../../packages/server/src/services/hermes/hermes-process'
|
||||
|
||||
const originalHome = process.env.HERMES_HOME
|
||||
let home: string | undefined
|
||||
|
||||
afterEach(async () => {
|
||||
if (originalHome === undefined) delete process.env.HERMES_HOME
|
||||
else process.env.HERMES_HOME = originalHome
|
||||
if (home) await rm(home, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
describe('disk profile inventory', () => {
|
||||
it('reads models and active selection for 20 profiles without scanning histories or skills', async () => {
|
||||
home = await mkdtemp(join(tmpdir(), 'hermes-profile-snapshot-'))
|
||||
process.env.HERMES_HOME = home
|
||||
await writeFile(join(home, 'config.yaml'), 'model: default-model\n')
|
||||
await writeFile(join(home, 'active_profile'), 'perf-19\n')
|
||||
for (let index = 0; index < 20; index++) {
|
||||
const dir = join(home, 'profiles', `perf-${index}`)
|
||||
await mkdir(dir, { recursive: true })
|
||||
await writeFile(join(dir, 'config.yaml'), `model:\n default: model-${index}\n`)
|
||||
// These are deliberately invalid; inventory must not parse session history.
|
||||
await writeFile(join(dir, 'state.db'), 'not a database')
|
||||
}
|
||||
const started = performance.now()
|
||||
const profiles = listProfilesSnapshot()
|
||||
console.info(`Disk inventory for 21 profiles: ${(performance.now() - started).toFixed(2)} ms`)
|
||||
expect(profiles).toHaveLength(21)
|
||||
expect(profiles.find(profile => profile.name === 'perf-19')).toMatchObject({ model: 'model-19', active: true })
|
||||
expect(profiles[0]).toMatchObject({ name: 'default', model: 'default-model', active: false })
|
||||
expect(execHermesWithBin).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -27,6 +27,7 @@ const gatewayAutostartMocks = vi.hoisted(() => ({
|
||||
// Mock hermes-cli
|
||||
vi.mock('../../packages/server/src/services/hermes/hermes-cli', () => ({
|
||||
listProfiles: vi.fn(),
|
||||
listProfilesSnapshot: vi.fn(),
|
||||
getProfile: vi.fn(),
|
||||
createProfile: vi.fn(),
|
||||
deleteProfile: vi.fn(),
|
||||
@@ -313,6 +314,28 @@ describe('Profile Routes', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('lists 20 profiles without waiting for CLI or embedding uploaded images', async () => {
|
||||
const webUiHome = await mkdtemp(join(tmpdir(), 'hermes-ui-profile-list-'))
|
||||
tempHomes.push(webUiHome)
|
||||
process.env.HERMES_WEB_UI_HOME = webUiHome
|
||||
const { list, updateAvatar, getAvatar } = await import('../../packages/server/src/controllers/hermes/profiles')
|
||||
await updateAvatar({ params: { name: 'work-0' }, request: { body: { type: 'image', dataUrl: 'data:image/png;base64,YQ==' } } })
|
||||
vi.mocked(hermesCli.listProfiles).mockReturnValue(new Promise(() => {}))
|
||||
vi.mocked(hermesCli.listProfilesSnapshot).mockReturnValue(Array.from({ length: 20 }, (_, index) => ({ name: `work-${index}`, active: false, model: 'test', alias: '' })))
|
||||
const ctx: any = { state: { profile: { name: 'work-0' } } }
|
||||
await list(ctx)
|
||||
expect(hermesCli.listProfiles).not.toHaveBeenCalled()
|
||||
expect(ctx.body.profiles).toHaveLength(20)
|
||||
expect(ctx.body.profiles[0]).toMatchObject({ active: true, avatar: { type: 'image' } })
|
||||
expect(ctx.body.profiles[0].avatar.dataUrl).toBeUndefined()
|
||||
const imageCtx: any = { params: { name: 'work-0' } }
|
||||
await getAvatar(imageCtx)
|
||||
expect(imageCtx.body.avatar.dataUrl).toBe('data:image/png;base64,YQ==')
|
||||
const denied: any = { params: { name: 'work-0' }, state: { user: { id: -123, role: 'admin' } } }
|
||||
await getAvatar(denied)
|
||||
expect(denied.status).toBe(403)
|
||||
})
|
||||
|
||||
describe('profile avatars', () => {
|
||||
it('stores generated avatar metadata under the Web UI home', async () => {
|
||||
const webUiHome = await mkdtemp(join(tmpdir(), 'hermes-ui-avatar-'))
|
||||
|
||||
Reference in New Issue
Block a user