test(desktop): cover personalityOptions' root read and pin GUI ordering

Address review on #123378:
- Add enumOptionsFor('display.personality', …) tests with a root-level
  `personalities` block, deriving expected built-ins from
  BUILTIN_PERSONALITIES (change-detector rule). Reverting the new root
  read now fails these, closing the coverage gap the reviewer flagged.
- Replace the Set-based clash assertion in personalityNamesFromConfig
  with direct array equality so it pins membership, dedupe, and the
  root-before-agent ordering the CLI listing uses.
This commit is contained in:
PRATHAMESH75
2026-09-26 15:16:17 +05:30
committed by brooklyn!
parent 9992d70f0d
commit afb924d841
2 changed files with 47 additions and 1 deletions

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
import type { HermesConfigRecord } from '@/types/hermes'
import { BUILTIN_PERSONALITIES } from './constants'
import { defineFieldCopy, fieldCopyForSchemaKey, schemaKeyToFieldCopyKey } from './field-copy'
import {
clearsEnabledToolsets,
@@ -266,6 +267,47 @@ describe('settings helpers', () => {
})
})
describe('enumOptionsFor — display.personality dropdown', () => {
it('lists a root-level `personalities` block alongside the built-ins (#123297)', () => {
// The Python spec (`hermes_cli.personality.available_personalities`) overlays
// the built-ins with the root `personalities` block then `agent.personalities`;
// the dropdown must surface a root-registered persona the CLI/gateway resolve.
const config: HermesConfigRecord = { personalities: { root_persona: { prompt: 'hi' } } }
const opts = enumOptionsFor('display.personality', '', config)
// Derive the expected built-ins from the source of truth, per the repo's
// change-detector rule — adding a built-in must not silently break this.
for (const builtin of BUILTIN_PERSONALITIES) {
expect(opts).toContain(builtin)
}
expect(opts).toContain('') // the "unset" sentinel
expect(opts).toContain('root_persona')
})
it('merges root and agent personalities, deduping a clashing name', () => {
const config: HermesConfigRecord = {
personalities: { root_persona: {}, shared: {} },
agent: { personalities: { agent_persona: {}, shared: {} } }
}
const opts = enumOptionsFor('display.personality', '', config)!
expect(opts).toContain('root_persona')
expect(opts).toContain('agent_persona')
// a name in both blocks is offered exactly once
expect(opts.filter(o => o === 'shared')).toHaveLength(1)
})
it('ignores a non-object or array `personalities` block', () => {
for (const bad of [[], 'nope', 42, null]) {
const opts = enumOptionsFor('display.personality', '', { personalities: bad } as HermesConfigRecord)!
// still the built-ins + empty sentinel, no crash on a malformed block
expect(opts).toContain('')
for (const builtin of BUILTIN_PERSONALITIES) {
expect(opts).toContain(builtin)
}
}
})
})
describe('sectionFieldEntries', () => {
it('renders memory.provider from config even when the backend schema omits it', () => {
const schema = { 'memory.memory_enabled': { type: 'boolean' as const } }

View File

@@ -263,7 +263,11 @@ describe('personalityNamesFromConfig', () => {
agent: { personalities: { agent_persona: 'a', shared: 'agent' } }
})
expect(new Set(names)).toEqual(new Set(['root_persona', 'shared', 'agent_persona']))
// Direct array equality pins membership, dedupe, AND order in one assertion:
// `available_personalities()` inserts the root block before `agent.personalities`,
// and a clashing name keeps its first-insert (root) position, so the GUI listing
// must match that exact order.
expect(names).toEqual(['root_persona', 'shared', 'agent_persona'])
})
it('ignores non-object or array blocks', () => {