Files
gongxue-base/apps/server/src/agent-context/business-context.tools.spec.ts
wangziqi 24e0ecbdaf feat(ai): 业务上下文感知与 A2UI 链路统一
- 新增代码内业务上下文元数据层(实体字典 + 三大闭环工作流)
- 新增 get_business_context / get_entity_schema / get_pending_tasks 运行时工具
- SYSTEM_PROMPT 与技能目录改为先查业务流程/待办再执行
- A2UI 增加 ai_a2ui_submissions 幂等表、表单过期、ui.artifact 事件
- 提交回灌携带 submissionId / fieldErrors / 下一步建议
- 前端 uiArtifacts 归一化与过期表单禁用
2026-08-06 15:23:23 +08:00

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');
});
});