workspaceGateway.test.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. // @vitest-environment node
  2. import {expect,it} from 'vitest'
  3. import {WebSocketServer,type WebSocket} from 'ws'
  4. import {WorkspaceGateway,type GatewayFrame,type GatewayTarget} from '../../src/server/workspaceGateway'
  5. it('adapts Hermes server requests, scopes answers, restores pending cards and withdraws cancelled requests',async()=>{
  6. const server=new WebSocketServer({port:0,host:'127.0.0.1'})
  7. await new Promise<void>(done=>server.once('listening',done))
  8. const received:any[]=[],events:GatewayFrame[]=[]
  9. let socket:WebSocket
  10. server.on('connection',connection=>{
  11. socket=connection
  12. connection.send(JSON.stringify({method:'event',params:{type:'gateway.ready'}}))
  13. connection.on('message',raw=>{
  14. const value=JSON.parse(String(raw));received.push(value)
  15. if(value.method==='session.resume')connection.send(JSON.stringify({id:value.id,result:{session_id:'session',open_requests:[
  16. {id:'srq-replay',method:'clarify',params:{session_id:'session',question:'下一步做什么?'}},
  17. ]}}))
  18. })
  19. })
  20. const target={url:new URL(`http://127.0.0.1:${(server.address() as {port:number}).port}`),
  21. client:{directAgent:undefined},session:{webSocketCredential:async()=>({name:'ticket',value:'fixture'})}} as unknown as GatewayTarget
  22. const gateway=new WorkspaceGateway(target);gateway.onEvent=frame=>events.push(frame)
  23. const send=(value:unknown)=>socket.send(JSON.stringify(value))
  24. try{
  25. await gateway.connect()
  26. send({id:'srq-approve',method:'approval',params:{session_id:'session',request_id:'queue-id',description:'需要读取授权文件',choices:['once','deny']}})
  27. await expect.poll(()=>events.some(e=>e.type==='approval.requested')).toBe(true)
  28. expect(events.at(-1)).toMatchObject({session_id:'session',payload:{request_id:'srq-approve',message:'需要读取授权文件'}})
  29. await expect(gateway.rpc('approval.respond',{session_id:'another',request_id:'srq-approve',choice:'once'})).rejects.toMatchObject({code:'gateway_rejected'})
  30. await gateway.rpc('approval.respond',{session_id:'session',request_id:'srq-approve',choice:'deny'})
  31. await expect.poll(()=>received.some(r=>r.id==='srq-approve')).toBe(true)
  32. expect(received.find(r=>r.id==='srq-approve')).toEqual({jsonrpc:'2.0',id:'srq-approve',result:{choice:'deny'}})
  33. await expect(gateway.rpc('approval.respond',{session_id:'session',request_id:'srq-approve',choice:'once'})).rejects.toMatchObject({code:'gateway_rejected'})
  34. await gateway.rpc('session.resume',{session_id:'session'})
  35. await expect.poll(()=>events.some(e=>e.type==='clarify.requested')).toBe(true)
  36. send({method:'event',params:{type:'request.cancel',session_id:'session',payload:{id:'srq-replay',method:'clarify',reason:'interrupted'}}})
  37. await expect.poll(()=>events.some(e=>e.type==='interaction.cancelled')).toBe(true)
  38. await expect(gateway.rpc('clarify.respond',{session_id:'session',request_id:'srq-replay',answer:'late'})).rejects.toMatchObject({code:'gateway_rejected'})
  39. send({id:'srq-unknown',method:'unsupported.prompt',params:{session_id:'session'}})
  40. await expect.poll(()=>received.some(r=>r.id==='srq-unknown')).toBe(true)
  41. expect(received.find(r=>r.id==='srq-unknown').error.code).toBe(-32601)
  42. }finally{
  43. gateway.close();for(const client of server.clients)client.terminate()
  44. await new Promise<void>(done=>server.close(()=>done()))
  45. }
  46. })