test: cover project instruction configuration branches

This commit is contained in:
Yichen Jiang
2026-06-29 10:58:43 +08:00
parent 86519889c5
commit 2b99d8f5c2
5 changed files with 84 additions and 0 deletions

View File

@@ -128,6 +128,16 @@ describe('dsh-agent-core bundle', () => {
}
})
it('supports direct apply with project instructions disabled and no forwarded agents', async () => {
const ctx = new Context()
agentCore.apply(ctx, { projectInstructions: false })
await new Promise(resolve => setTimeout(resolve, 50))
expect(ctx.get('agents')?.list()).toEqual([])
expect(ctx.get('systemPrompt')).toBeDefined()
await ctx.fiber.dispose()
})
it('re-exports the loop config schema as its own', () => {
expect(agentCore.Config).toBeDefined()
expect(agentCore.name).toBe('agent-core')

View File

@@ -150,6 +150,7 @@ function ancestorChain(root: string, cwd: string): string[] {
while (current !== resolvedRoot) {
chain.push(current)
const parent = dirname(current)
/* v8 ignore next -- defensive guard for direct helper misuse; discovery always passes cwd or an ancestor root. */
if (parent === current) break
current = parent
}

View File

@@ -195,6 +195,24 @@ describe('project instruction discovery', () => {
}
})
it('labels the default DSH home as ~/.dsh when HOME points at the configured default', async () => {
const root = await tempRepo()
const home = await tempRepo()
const previousHome = process.env.HOME
try {
process.env.HOME = home
await write(join(home, '.dsh/AGENTS.md'), 'global default rule')
const files = await discoverBaselineInstructionFiles({ cwd: root })
expect(files.map(file => file.displayPath)).toEqual(['~/.dsh/AGENTS.md'])
} finally {
process.env.HOME = previousHome
await rm(root, { recursive: true, force: true })
await rm(home, { recursive: true, force: true })
}
})
it('ignores instruction candidates that are directories', async () => {
const root = await tempRepo()
const home = await tempRepo()
@@ -280,6 +298,38 @@ describe('project instruction rendering', () => {
expect(rendered.truncated[0]!.includedBytes).toBeGreaterThan(0)
expect(Buffer.byteLength(rendered.text, 'utf8')).toBeLessThanOrEqual(700)
})
it('omits all text when the render budget is disabled', () => {
const rendered = renderProjectInstructions([
{ absolutePath: '/repo/AGENTS.md', displayPath: 'AGENTS.md', content: 'root rules' },
], { maxBytes: 0 })
expect(rendered).toEqual({
text: '',
omitted: [{ absolutePath: '/repo/AGENTS.md', displayPath: 'AGENTS.md', content: 'root rules' }],
truncated: [],
})
})
it('falls back to a compact truncation notice when even the empty heading cannot fit', () => {
const rendered = renderProjectInstructions([
{ absolutePath: '/repo/pkg/AGENTS.md', displayPath: 'pkg/AGENTS.md', content: 'x'.repeat(1000) },
], { maxBytes: 100 })
expect(rendered.text).toBe('<!-- Project instruction budget 100 bytes: truncated pkg/AGENTS.md from 1000 to 0 bytes -->')
expect(rendered.truncated).toEqual([{ displayPath: 'pkg/AGENTS.md', originalBytes: 1000, includedBytes: 0 }])
expect(Buffer.byteLength(rendered.text, 'utf8')).toBeLessThanOrEqual(100)
})
it('truncates the compact notice itself when the render budget is smaller than the notice', () => {
const rendered = renderProjectInstructions([
{ absolutePath: '/repo/pkg/AGENTS.md', displayPath: 'pkg/AGENTS.md', content: 'x'.repeat(1000) },
], { maxBytes: 20 })
expect(rendered.text).toBe('<!-- Project instruc')
expect(rendered.truncated).toEqual([{ displayPath: 'pkg/AGENTS.md', originalBytes: 1000, includedBytes: 0 }])
expect(Buffer.byteLength(rendered.text, 'utf8')).toBe(20)
})
})
describe('project instruction request injection', () => {

View File

@@ -46,6 +46,18 @@ describe('dsh-acp-agent composition', () => {
await ctx.fiber.dispose()
})
it('forwards explicit project-instruction controls to the bundled spine', async () => {
const ctx = await mount({
model: 'mock',
systemPrompt: 'hi',
persistenceRoot: '/tmp/dsh-acp-agent-project-instructions',
projectInstructions: false,
})
expect(ctx.get('agents')).toBeDefined()
expect(ctx.get('agentLoop')).toBeDefined()
await ctx.fiber.dispose()
})
it('exposes its plugin shape', () => {
expect(acpAgent.name).toBe('acp-agent')
expect(acpAgent.Config).toBeDefined()

View File

@@ -51,6 +51,17 @@ describe('dsh-stdio-agent app', () => {
await ctx.fiber.dispose()
})
it('forwards explicit project-instruction controls to the bundled spine', async () => {
const ctx = await mount({
model: 'mock',
systemPrompt: 'hi',
persistenceRoot: '/tmp/dsh-stdio-agent-spec-project-instructions',
projectInstructions: false,
})
expect(ctx.get('agents')?.get(AgentId('main'))).toBeDefined()
await ctx.fiber.dispose()
})
it('forwards resumeSessionId onto the pre-created agent when set', async () => {
// A resume id defers agent creation until persistence loads; with no backing
// session the resume is contained + logged, so no `main` agent registers —