|
@@ -2,7 +2,8 @@
|
|
|
import { mkdtempSync, rmSync, readFileSync } from 'node:fs'
|
|
import { mkdtempSync, rmSync, readFileSync } from 'node:fs'
|
|
|
import { tmpdir } from 'node:os'
|
|
import { tmpdir } from 'node:os'
|
|
|
import { join } from 'node:path'
|
|
import { join } from 'node:path'
|
|
|
-import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
|
|
|
|
|
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
|
+import { randomUUID } from 'node:crypto'
|
|
|
import request from 'supertest'
|
|
import request from 'supertest'
|
|
|
import type Koa from 'koa'
|
|
import type Koa from 'koa'
|
|
|
import { createApplication, type ApplicationRuntime } from '../../src/server/app'
|
|
import { createApplication, type ApplicationRuntime } from '../../src/server/app'
|
|
@@ -94,6 +95,24 @@ afterEach(() => {
|
|
|
rmSync(home, { recursive: true, force: true })
|
|
rmSync(home, { recursive: true, force: true })
|
|
|
})
|
|
})
|
|
|
describe('application workspace HTTP contract', () => {
|
|
describe('application workspace HTTP contract', () => {
|
|
|
|
|
+ it('resumes a stopped goal from a real user request without duplicating its run',async()=>{
|
|
|
|
|
+ vi.spyOn(runtime.workspaceRuntime,'wake').mockImplementation(()=>{})
|
|
|
|
|
+ vi.spyOn(runtime.workspaceRuntime.teamTools,'requireAvailable').mockResolvedValue()
|
|
|
|
|
+ const lead=runtime.workspace.createAgent('first',{name:'任务负责人',profile:'default',canManageTeam:true})
|
|
|
|
|
+ const member=runtime.workspace.createAgent('first',{name:'成员',profile:'default'})
|
|
|
|
|
+ const source=runtime.workspace.list<any>('first','conversation').find(c=>c.kind==='direct'&&c.memberIds[0]===lead.id)!
|
|
|
|
|
+ const original=runtime.workspaceRuntime.send('first',source.id,{requestId:randomUUID(),content:'完成任务'})
|
|
|
|
|
+ const team=runtime.workspace.createGroup('first',{name:'任务组',memberIds:[lead.id,member.id],administratorId:lead.id})
|
|
|
|
|
+ const task=runtime.workspace.tasks('first',team.id)[0]!
|
|
|
|
|
+ runtime.workspaceRuntime.tasks.begin('first',task,lead,'完成任务',{conversationId:source.id,runId:original.id,agentId:lead.id})
|
|
|
|
|
+ await req('post',`/api/app/conversations/${team.id}/tasks/${task.id}/stop`).send({}).expect(200)
|
|
|
|
|
+ const input={requestId:randomUUID()}
|
|
|
|
|
+ const resumed=await req('post',`/api/app/conversations/${team.id}/tasks/${task.id}/resume`).send(input).expect(200)
|
|
|
|
|
+ expect(resumed.body.goal.status).toBe('running')
|
|
|
|
|
+ const repeated=await req('post',`/api/app/conversations/${team.id}/tasks/${task.id}/resume`).send(input).expect(200)
|
|
|
|
|
+ expect(repeated.body.run.id).toBe(resumed.body.run.id)
|
|
|
|
|
+ await req('post',`/api/app/conversations/${team.id}/tasks/${task.id}/resume`,'second').send({requestId:randomUUID()}).expect(404)
|
|
|
|
|
+ })
|
|
|
it('grants team management only with a ready bridge and persists explicit revocation', async () => {
|
|
it('grants team management only with a ready bridge and persists explicit revocation', async () => {
|
|
|
const denied = await req('post','/api/app/agents').send({name:'老板',profile:'default',canManageTeam:true}).expect(409)
|
|
const denied = await req('post','/api/app/agents').send({name:'老板',profile:'default',canManageTeam:true}).expect(409)
|
|
|
expect(denied.body.code).toBe('team_tools_unavailable')
|
|
expect(denied.body.code).toBe('team_tools_unavailable')
|