test: cover the agent-preset wire routes and both restore edges
master moved the RPC surface onto the fetch transport, so the two agentPreset methods arrived there with nothing driving them. Adds the round trip, plus the two `recompose` edges that had none: an agent that never composed a preset, so there is nothing to restore, and a restore that fails because the composition it reaches for is gone — the roster is a live directory. Marks the deliberate non-Error rejections in the store specs, which is the branch they exist to cover.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises'
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
@@ -331,6 +331,53 @@ describe('replacing a composition', () => {
|
||||
expect(toolNames(ctx, handle.agent)).toEqual(['alpha'])
|
||||
})
|
||||
|
||||
it('leaves an agent that never composed one with nothing to restore', async () => {
|
||||
const handle = await ctx.agents.create({ sessionId: SessionId('sess-bare') })
|
||||
|
||||
await expect(ctx.agentPresets.recompose(handle.agent.ctx, 'broken'))
|
||||
.rejects.toThrow(/failed to mount/)
|
||||
|
||||
// There was no previous composition to put back, so the failure is the
|
||||
// whole outcome — not a restore that silently invents one.
|
||||
expect(toolNames(ctx, handle.agent)).toEqual([])
|
||||
})
|
||||
|
||||
it('reports the switch failure when the restore fails too', async () => {
|
||||
// A preset root this test owns, so removing the composition mid-flight
|
||||
// cannot disturb the shipped fixtures.
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-preset-restore-'))
|
||||
const seeded: [string, string][] = [['first', `- id: only\n name: ${join(FIXTURES, 'plugins', 'contribute.js')}\n config:\n tool: only\n`], ['broken', '- id: nope\n name: ./does-not-exist.js\n']]
|
||||
for (const [id, body] of seeded) {
|
||||
await mkdir(join(root, id))
|
||||
await writeFile(join(root, id, COMPOSITION_FILE), body)
|
||||
}
|
||||
const scoped = new Context()
|
||||
scoped.baseUrl = pathToFileURL(FIXTURES).href + '/'
|
||||
await scoped.plugin(Loader)
|
||||
scoped.loader.builtins.include = Include
|
||||
await scoped.plugin(LlmService)
|
||||
await scoped.plugin(SessionStore)
|
||||
await scoped.plugin(SystemPrompt, { persona: '' })
|
||||
await scoped.plugin(ToolRegistry)
|
||||
await scoped.plugin(AgentRegistry)
|
||||
await scoped.plugin(AgentLoop, { agents: [] })
|
||||
await scoped.plugin(AgentPresets, { default: 'first', roots: [{ path: root, trust: 'user' as const }] })
|
||||
const handle = await scoped.agents.create({
|
||||
sessionId: SessionId('sess-restore-gone'),
|
||||
setup: async (agentCtx: Context) => void await scoped.agentPresets.mount(agentCtx, 'first'),
|
||||
})
|
||||
|
||||
// The roster is a live directory: the composition the agent came from can
|
||||
// be gone by the time the restore reaches for it.
|
||||
await rm(join(root, 'first'), { recursive: true })
|
||||
|
||||
await expect(scoped.agentPresets.recompose(handle.agent.ctx, 'broken'))
|
||||
.rejects.toThrow(/failed to mount/)
|
||||
|
||||
// The switch failure is the actionable one; the restore's is swallowed.
|
||||
expect(toolNames(scoped, handle.agent)).toEqual([])
|
||||
})
|
||||
|
||||
it('refuses an unscoped context', async () => {
|
||||
await expect(ctx.agentPresets.recompose(ctx, 'minimal'))
|
||||
.rejects.toThrow(/unscoped context/)
|
||||
|
||||
Reference in New Issue
Block a user