mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-05-25 13:30:14 +00:00
f61a1d9454
* docs release 0.6.0 changelog * fix auth startup and profile model defaults
123 lines
4.2 KiB
TypeScript
123 lines
4.2 KiB
TypeScript
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises'
|
|
import { tmpdir } from 'os'
|
|
import { join } from 'path'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import YAML from 'js-yaml'
|
|
|
|
vi.mock('../../packages/server/src/services/hermes/hermes-cli', () => ({
|
|
pinSkill: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('../../packages/server/src/db/hermes/sessions-db', () => ({
|
|
getSkillUsageStatsFromDb: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('../../packages/server/src/db', () => ({
|
|
getDb: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('../../packages/server/src/db/hermes/schemas', () => ({
|
|
MODEL_CONTEXT_TABLE: 'model_context',
|
|
}))
|
|
|
|
const originalHermesHome = process.env.HERMES_HOME
|
|
const tempHomes: string[] = []
|
|
let hermesHome = ''
|
|
|
|
async function loadModelsController() {
|
|
vi.resetModules()
|
|
process.env.HERMES_HOME = hermesHome
|
|
return import('../../packages/server/src/controllers/hermes/models')
|
|
}
|
|
|
|
async function loadSkillsController() {
|
|
vi.resetModules()
|
|
process.env.HERMES_HOME = hermesHome
|
|
return import('../../packages/server/src/controllers/hermes/skills')
|
|
}
|
|
|
|
function makeCtx(body: unknown): any {
|
|
return {
|
|
request: { body },
|
|
status: 200,
|
|
body: undefined,
|
|
query: {},
|
|
params: {},
|
|
state: {},
|
|
get: vi.fn(() => ''),
|
|
}
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
hermesHome = await mkdtemp(join(tmpdir(), 'hermes-config-controller-'))
|
|
tempHomes.push(hermesHome)
|
|
await mkdir(hermesHome, { recursive: true })
|
|
})
|
|
|
|
afterEach(async () => {
|
|
vi.resetModules()
|
|
if (originalHermesHome === undefined) delete process.env.HERMES_HOME
|
|
else process.env.HERMES_HOME = originalHermesHome
|
|
await Promise.all(tempHomes.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
|
|
hermesHome = ''
|
|
})
|
|
|
|
describe('config mutating controllers', () => {
|
|
it('setConfigModel updates only the model section and preserves existing config', async () => {
|
|
await writeFile(join(hermesHome, 'config.yaml'), [
|
|
'terminal:',
|
|
' backend: local',
|
|
'model:',
|
|
' default: old',
|
|
' provider: old-provider',
|
|
'',
|
|
].join('\n'), 'utf-8')
|
|
const { setConfigModel } = await loadModelsController()
|
|
const ctx = makeCtx({ default: 'glm-5.1', provider: 'custom:glm' })
|
|
|
|
await setConfigModel(ctx)
|
|
|
|
expect(ctx.body).toEqual({ success: true })
|
|
const config = YAML.load(await readFile(join(hermesHome, 'config.yaml'), 'utf-8')) as any
|
|
expect(config.model).toEqual({ default: 'glm-5.1', provider: 'custom:glm' })
|
|
expect(config.terminal.backend).toBe('local')
|
|
})
|
|
|
|
it('setConfigModel uses the requested profile header when auth has not populated state.profile', async () => {
|
|
const researchDir = join(hermesHome, 'profiles', 'research')
|
|
await mkdir(researchDir, { recursive: true })
|
|
await writeFile(join(hermesHome, 'config.yaml'), 'model:\n default: root-model\n', 'utf-8')
|
|
await writeFile(join(researchDir, 'config.yaml'), 'model:\n default: old-research\n', 'utf-8')
|
|
const { setConfigModel } = await loadModelsController()
|
|
const ctx = makeCtx({ default: 'research-model', provider: 'deepseek' })
|
|
ctx.get = vi.fn((name: string) => name.toLowerCase() === 'x-hermes-profile' ? 'research' : '')
|
|
|
|
await setConfigModel(ctx)
|
|
|
|
expect(ctx.body).toEqual({ success: true })
|
|
const rootConfig = YAML.load(await readFile(join(hermesHome, 'config.yaml'), 'utf-8')) as any
|
|
const researchConfig = YAML.load(await readFile(join(researchDir, 'config.yaml'), 'utf-8')) as any
|
|
expect(rootConfig.model.default).toBe('root-model')
|
|
expect(researchConfig.model).toEqual({ default: 'research-model', provider: 'deepseek' })
|
|
})
|
|
|
|
it('skill toggle preserves unrelated config while adding and removing disabled skills', async () => {
|
|
await writeFile(join(hermesHome, 'config.yaml'), [
|
|
'model:',
|
|
' default: glm-5.1',
|
|
'skills:',
|
|
' disabled:',
|
|
' - old-skill',
|
|
'',
|
|
].join('\n'), 'utf-8')
|
|
const { toggle } = await loadSkillsController()
|
|
|
|
await toggle(makeCtx({ name: 'new-skill', enabled: false }))
|
|
await toggle(makeCtx({ name: 'old-skill', enabled: true }))
|
|
|
|
const config = YAML.load(await readFile(join(hermesHome, 'config.yaml'), 'utf-8')) as any
|
|
expect(config.model.default).toBe('glm-5.1')
|
|
expect(config.skills.disabled).toEqual(['new-skill'])
|
|
})
|
|
})
|