test: cover new defensive branches
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user