|
@@ -242,6 +242,35 @@ describe('loopback tool lease', () => {
|
|
|
expect((await http('/tools/list',{})).status).toBe(403)
|
|
expect((await http('/tools/list',{})).status).toBe(403)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ it('reports the underlying error message instead of masking tool failures', async () => {
|
|
|
|
|
+ const lease = await createWorkspaceToolLease({
|
|
|
|
|
+ target, profile:manager.profile, workId:work.id,
|
|
|
|
|
+ session:()=>({runtimeId:'runtime',storedId:'stored'}),signal:new AbortController().signal,
|
|
|
|
|
+ assertActive:()=>{},catalog:()=>[{id:'workspace_probe',name:'workspace_probe',description:'probe',inputSchema:{}}],
|
|
|
|
|
+ call:async()=>{throw new Error('虚拟机磁盘已满')},onFailure:()=>{},
|
|
|
|
|
+ })
|
|
|
|
|
+ leases.push(lease); await lease.bind()
|
|
|
|
|
+ const catalog = await (await http('/tools/list',{})).json()
|
|
|
|
|
+ const result = await (await http('/tools/call',{toolId:catalog.tools[0].id,arguments:{},callId:'probe'})).json()
|
|
|
|
|
+ expect(result.isError).toBe(true)
|
|
|
|
|
+ expect(JSON.parse(result.content[0].text)).toMatchObject({error:'虚拟机磁盘已满',code:'team_tool_failed'})
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('keeps plain authorization stop messages readable', async () => {
|
|
|
|
|
+ let active = true
|
|
|
|
|
+ const lease = await createWorkspaceToolLease({
|
|
|
|
|
+ target, profile:manager.profile, workId:work.id,
|
|
|
|
|
+ session:()=>({runtimeId:'runtime',storedId:'stored'}),signal:new AbortController().signal,
|
|
|
|
|
+ assertActive:()=>{if(!active)throw new Error('运行已停止')},
|
|
|
|
|
+ catalog:()=>[],call:async()=>({}),onFailure:()=>{},
|
|
|
|
|
+ })
|
|
|
|
|
+ leases.push(lease); await lease.bind()
|
|
|
|
|
+ active = false
|
|
|
|
|
+ const response = await http('/tools/list',{})
|
|
|
|
|
+ expect(response.status).toBe(500)
|
|
|
|
|
+ expect(await response.json()).toMatchObject({error:'运行已停止',code:'team_tool_failed'})
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
it('checks readiness per Profile and rejects unavailable or remote bridges', async () => {
|
|
it('checks readiness per Profile and rejects unavailable or remote bridges', async () => {
|
|
|
await requireTeamToolBridge(target,'writer')
|
|
await requireTeamToolBridge(target,'writer')
|
|
|
expect(target.session.request).toHaveBeenCalledWith(expect.stringContaining('/capabilities'),expect.objectContaining({search:new URLSearchParams({profile:'writer'})}))
|
|
expect(target.session.request).toHaveBeenCalledWith(expect.stringContaining('/capabilities'),expect.objectContaining({search:new URLSearchParams({profile:'writer'})}))
|