- 新增代码内业务上下文元数据层(实体字典 + 三大闭环工作流) - 新增 get_business_context / get_entity_schema / get_pending_tasks 运行时工具 - SYSTEM_PROMPT 与技能目录改为先查业务流程/待办再执行 - A2UI 增加 ai_a2ui_submissions 幂等表、表单过期、ui.artifact 事件 - 提交回灌携带 submissionId / fieldErrors / 下一步建议 - 前端 uiArtifacts 归一化与过期表单禁用
74 lines
3.2 KiB
TypeScript
74 lines
3.2 KiB
TypeScript
import { describe, expect, it, jest } from '@jest/globals';
|
|
import type { AgentToolContext } from '../agent-tools/agent-tool.types';
|
|
import { AgentToolContextFactory } from '../agent-tools/agent-tool.types';
|
|
import type { AuthenticatedUser } from '../authorization';
|
|
import type { BusinessContextService } from './business-context.service';
|
|
import { GetBusinessContextTool, GetEntitySchemaTool } from './get-business-context.tool';
|
|
import { GetPendingTasksTool } from './get-pending-tasks.tool';
|
|
|
|
function context(permissions: string[] = []): AgentToolContext {
|
|
const user: AuthenticatedUser = {
|
|
id: 7,
|
|
username: 'ops',
|
|
permissions,
|
|
isSuperAdmin: false,
|
|
roles: [],
|
|
};
|
|
return AgentToolContextFactory.fromAuthenticatedUser(user);
|
|
}
|
|
|
|
describe('agent business context tools', () => {
|
|
it('get_business_context 校验 workflowKey 并透传主体与过滤条件', async () => {
|
|
const service = {
|
|
getBusinessContext: jest.fn().mockReturnValue({ workflows: [], entities: [] }),
|
|
} as unknown as BusinessContextService;
|
|
const tool = new GetBusinessContextTool(service);
|
|
expect(tool.requiredPermission).toBe('ai:chat:use');
|
|
expect(tool.validate({ workflowKey: 123 }).ok).toBe(false);
|
|
expect(tool.validate({ workflowKey: 'x'.repeat(51) }).ok).toBe(false);
|
|
expect(tool.validate({ debug: true }).ok).toBe(false);
|
|
const parsed = tool.validate({ workflowKey: 'dormitory_billing' });
|
|
expect(parsed.ok).toBe(true);
|
|
if (!parsed.ok) return;
|
|
const ctx = context(['occupancy:view']);
|
|
await tool.execute(parsed.value, ctx);
|
|
expect(service.getBusinessContext).toHaveBeenCalledWith(
|
|
{ permissions: ['occupancy:view'], isSuperAdmin: false },
|
|
'dormitory_billing',
|
|
);
|
|
});
|
|
|
|
it('get_entity_schema 必须提供存在的 entityKey', async () => {
|
|
const service = {
|
|
getEntitySchema: jest.fn().mockReturnValue({ key: 'student', name: '学生档案' }),
|
|
} as unknown as BusinessContextService;
|
|
const tool = new GetEntitySchemaTool(service);
|
|
expect(tool.validate({}).ok).toBe(false);
|
|
expect(tool.validate({ entityKey: '' }).ok).toBe(false);
|
|
expect(tool.validate({ entityKey: 'student', extra: 1 }).ok).toBe(false);
|
|
const parsed = tool.validate({ entityKey: 'student' });
|
|
expect(parsed.ok).toBe(true);
|
|
if (!parsed.ok) return;
|
|
await tool.execute(parsed.value, context());
|
|
expect(service.getEntitySchema).toHaveBeenCalledWith(
|
|
{ permissions: [], isSuperAdmin: false },
|
|
'student',
|
|
);
|
|
});
|
|
|
|
it('get_pending_tasks 只接受已知工作流 key', async () => {
|
|
const service = {
|
|
getPendingTasks: jest.fn().mockResolvedValue([]),
|
|
};
|
|
const tool = new GetPendingTasksTool(service as never);
|
|
expect(tool.requiredPermission).toBe('ai:chat:use');
|
|
expect(tool.validate({ workflowKey: 'unknown_loop' }).ok).toBe(false);
|
|
expect(tool.validate({ workflowKey: 'dormitory_billing', limit: 1 }).ok).toBe(false);
|
|
const parsed = tool.validate({ workflowKey: 'dormitory_billing' });
|
|
expect(parsed.ok).toBe(true);
|
|
if (!parsed.ok) return;
|
|
await tool.execute(parsed.value, context(['bill:view']));
|
|
expect(service.getPendingTasks).toHaveBeenCalledWith(expect.anything(), 'dormitory_billing');
|
|
});
|
|
});
|