test(todo): pin configurable policy boundaries

This commit is contained in:
Tianyi Cui
2026-07-29 20:43:31 +08:00
parent 20a388fc81
commit 70730b8669
8 changed files with 35 additions and 14 deletions

View File

@@ -44,6 +44,8 @@
"cordis": "^4.0.0-rc.7"
},
"devDependencies": {
"@cordisjs/plugin-include": "workspace:^",
"@cordisjs/plugin-loader": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-agent-loop": "workspace:^",
"@deepseek-ai/dsh-agent-loop-testkit": "workspace:^",

View File

@@ -1,6 +1,8 @@
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import SessionStore, { type Session, type SessionEvent } from '@deepseek-ai/dsh-session'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
import * as TodoInvariant from '@deepseek-ai/dsh-tool-todo/invariant'
import InvariantService from '@deepseek-ai/dsh-invariants'
@@ -17,14 +19,22 @@ function event(todos: unknown): SessionEvent {
}
describe('todo snapshot invariants', () => {
it('accepts a unique whole-list snapshot, including several active items', async () => {
const ctx = await setup()
expect(() => { ctx.emit('session/event', {} as Session, event([
it('accepts historical and live parallel snapshots under the single-active tool policy', async () => {
const todos = [
{ content: 'Inspect state', status: 'completed' },
{ content: 'Apply fix', status: 'in_progress' },
{ content: 'Watch background build', status: 'in_progress' },
{ content: 'Run checks', status: 'pending' },
])) }).not.toThrow()
] as const
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolTodo, { allowParallelInProgress: false })
ctx.sessions.create().append('todo/write', { todos: [...todos] })
await ctx.plugin(InvariantService, { enabled: true })
await expect(ctx.plugin(TodoInvariant).then(() => undefined)).resolves.toBeUndefined()
expect(() => { ctx.emit('session/event', {} as Session, event(todos)) }).not.toThrow()
})
it.each([

View File

@@ -124,20 +124,23 @@ describe('tool-todo real Loader composition through cordis.yml', () => {
expect(owner.session.events.findLast(e => e.type === 'todo/write')?.data.todos).toEqual(PARALLEL_TODOS)
}, 30_000)
it('fails loading when allowParallelInProgress is omitted', async () => {
it.each([
{ label: 'is omitted', configLines: [], failure: '$.allowParallelInProgress missing required value' },
{ label: 'is not boolean', configLines: [' allowParallelInProgress: "no"'], failure: '$.allowParallelInProgress' },
])('fails loading when allowParallelInProgress $label', async ({ configLines, failure }) => {
// loader.await() is all-settled; configuration failure leaves a FAILED
// entry and escapes as a late rejection for the host boot to report.
const rejections: unknown[] = []
const onUnhandled = (err: unknown): void => { rejections.push(err) }
process.on('unhandledRejection', onUnhandled)
try {
const ctx = await boot([])
const ctx = await boot(configLines)
const entry = [...ctx.loader.entries()].find(e => e.options.name === '@deepseek-ai/dsh-tool-todo')
expect(entry?.fiber?.state).toBe(FiberState.FAILED)
for (let i = 0; i < 100 && rejections.length === 0; i++) {
await new Promise(resolve => setTimeout(resolve, 10))
}
expect(rejections.map(String).join('\n')).toContain('$.allowParallelInProgress missing required value')
expect(rejections.map(String).join('\n')).toContain(failure)
} finally {
process.off('unhandledRejection', onUnhandled)
}