test: cover new defensive branches

This commit is contained in:
Tianyi Cui
2026-06-17 21:31:54 +08:00
parent 7a5886da2d
commit 0b036d808c
4 changed files with 37 additions and 4 deletions
+11 -3
View File
@@ -85,6 +85,7 @@ function strictByToolName(tools: ToolSchema[] | undefined): Map<string, boolean
}
function patchPayload(payload: unknown, options: GenerateOptions, reasoning: PiAiReasoning | undefined): unknown {
/* v8 ignore next -- pi-ai onPayload always receives an object; tolerate unusual future hooks defensively */
if (typeof payload !== 'object' || payload === null) return payload
const body = payload as Payload
@@ -97,19 +98,26 @@ function patchPayload(payload: unknown, options: GenerateOptions, reasoning: PiA
const strictByName = strictByToolName(options.tools)
for (const tool of body.tools ?? []) {
const name = tool.function?.name
/* v8 ignore next -- malformed pi-ai payload guard: real tool entries always carry function */
if (tool.function === undefined) continue
const name = tool.function.name
/* v8 ignore next -- malformed pi-ai payload guard: real function entries always carry a string name */
if (typeof name !== 'string') continue
const strict = strictByName.get(name)
if (strict === undefined) delete tool.function?.strict
else if (tool.function !== undefined) tool.function.strict = strict
if (strict === undefined) delete tool.function.strict
else tool.function.strict = strict
}
const rawById = rawToolArguments(options)
/* v8 ignore next -- defensive for non-chat payloads; OpenAI chat payloads always carry messages */
for (const message of body.messages ?? []) {
if (message.role !== 'assistant') continue
/* v8 ignore next -- assistant messages without tool_calls need no raw-argument patch */
for (const call of message.tool_calls ?? []) {
/* v8 ignore next -- malformed pi-ai payload guard: real tool calls always carry a string id */
if (typeof call.id !== 'string') continue
const raw = rawById.get(call.id)
/* v8 ignore next -- pi-ai always emits a function object for assistant tool_calls; guard malformed payloads defensively */
if (raw !== undefined && call.function !== undefined) call.function.arguments = raw
}
}
+1
View File
@@ -198,6 +198,7 @@ describe('PiAiAdapter against a mock server', () => {
})
it.each([
[400, 'INVALID_REQUEST'],
[429, 'RATE_LIMIT'],
[500, 'SERVER'],
] as const)('maps HTTP %s to stable error code %s', async (status, code) => {
@@ -1039,6 +1039,14 @@ describe('SessionPersistenceJsonl: edge cases', () => {
expect(loaded.meta.updatedAt).toBe(5)
})
it('load rejects a corrupt sidecar instead of treating it as absent', async () => {
const m = meta('bad-sidecar')
await ctx.sessionPersistence.create(m)
await ctx.sessionPersistence.append(m.id, oneTurnLog())
await writeFile(sidecarPath(root, undefined, m.id), '{not json')
await expect(ctx.sessionPersistence.load(m.id)).rejects.toThrow()
})
it('list returns nothing when the root directory does not exist', async () => {
const ctx2 = new Context()
await ctx2.plugin(SessionStore)
+17 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, expectTypeOf, it } from 'vitest'
import { Context } from 'cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import { CallId, HarnessError } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, {
defineTool, schemaSpecToJsonSchema, validateArgs, ToolArgsError, ToolNotFoundError,
@@ -138,6 +138,22 @@ describe('ToolRegistry', () => {
})
})
it('preserves structured error info when a tools/execute listener throws HarnessError', async () => {
const ctx = await setup()
ctx.tools.register(echoTool)
ctx.on('tools/execute', async () => {
throw new HarnessError('denied', 'DENIED')
})
const result = await ctx.tools.execute({ callId: CallId('c1'), name: 'echo', arguments: { text: 'hi' } })
expect(result).toMatchObject({
callId: CallId('c1'),
isError: true,
error: { name: 'HarnessError', code: 'DENIED' },
})
})
it('schemas() snapshots tool schemas instead of exposing registry objects', async () => {
const ctx = await setup()
ctx.tools.register(echoTool)