feat: 扩展 Agent 业务查询与学生增改工具
This commit is contained in:
124
apps/server/src/agent-tools/tools/update-students.tool.spec.ts
Normal file
124
apps/server/src/agent-tools/tools/update-students.tool.spec.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { NotFoundException } from '@nestjs/common';
|
||||
import { UpdateStudentsTool } from './update-students.tool';
|
||||
|
||||
function createTool(overrides: Record<string, unknown> = {}) {
|
||||
const studentsService = {
|
||||
update: jest.fn(async (id: number, dto: Record<string, unknown>) => ({
|
||||
id,
|
||||
name: dto.name ?? '学生',
|
||||
})),
|
||||
...(overrides.studentsService ?? {}),
|
||||
};
|
||||
const tool = new UpdateStudentsTool(studentsService as never);
|
||||
return { tool, studentsService };
|
||||
}
|
||||
|
||||
const validInput = {
|
||||
updates: [
|
||||
{ id: 201, name: '於嘉丽' },
|
||||
{ id: 172, name: '徐玚' },
|
||||
],
|
||||
};
|
||||
|
||||
describe('UpdateStudentsTool', () => {
|
||||
it('exposes student:edit permission and student skill', () => {
|
||||
const { tool } = createTool();
|
||||
expect(tool.name).toBe('update_students');
|
||||
expect(tool.skillKey).toBe('student');
|
||||
expect(tool.requiredPermission).toBe('student:edit');
|
||||
});
|
||||
|
||||
it('rejects forbidden, unknown, and empty input', () => {
|
||||
const { tool } = createTool();
|
||||
expect(tool.validate({ userId: 1 }).ok).toBe(false);
|
||||
expect(tool.validate({ updates: [], admin: true }).ok).toBe(false);
|
||||
expect(tool.validate({}).ok).toBe(false);
|
||||
expect(tool.validate({ updates: [] }).ok).toBe(false);
|
||||
expect(tool.validate({ updates: [{ id: 201 }] }).ok).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects invalid ids, duplicate ids, invalid fields, and oversized batches', () => {
|
||||
const { tool } = createTool();
|
||||
expect(tool.validate({ updates: [{ id: 0, name: 'A' }] }).ok).toBe(false);
|
||||
expect(tool.validate({ updates: [{ id: 'x', name: 'A' }] }).ok).toBe(false);
|
||||
expect(
|
||||
tool.validate({
|
||||
updates: [
|
||||
{ id: 201, name: 'A' },
|
||||
{ id: 201, name: 'B' },
|
||||
],
|
||||
}).ok,
|
||||
).toBe(false);
|
||||
expect(tool.validate({ updates: [{ id: 201, status: 'archived' }] }).ok).toBe(false);
|
||||
expect(tool.validate({ updates: [{ id: 201, name: 'A'.repeat(51) }] }).ok).toBe(false);
|
||||
expect(
|
||||
tool.validate({
|
||||
updates: Array.from({ length: 13 }, (_, index) => ({ id: index + 1, name: 'A' })),
|
||||
}).ok,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts all supported editable fields', () => {
|
||||
const { tool } = createTool();
|
||||
const result = tool.validate({
|
||||
updates: [
|
||||
{
|
||||
id: 201,
|
||||
name: '於嘉丽',
|
||||
studentNo: 'S201',
|
||||
phone: '13800138000',
|
||||
idNumber: 'ID201',
|
||||
gender: '女',
|
||||
ethnicity: '汉族',
|
||||
emergencyContact: '家长',
|
||||
emergencyPhone: '13900139000',
|
||||
organizationId: 2,
|
||||
supervisor: '王老师',
|
||||
status: 'active',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(result.ok).toBe(true);
|
||||
if (result.ok) {
|
||||
expect(result.value.updates[0]).toMatchObject({
|
||||
id: 201,
|
||||
name: '於嘉丽',
|
||||
organizationId: 2,
|
||||
status: 'active',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('updates every student and reports a safe summary', async () => {
|
||||
const { tool, studentsService } = createTool();
|
||||
const result = await tool.execute(validInput, {} as never);
|
||||
expect(studentsService.update).toHaveBeenCalledTimes(2);
|
||||
expect(studentsService.update).toHaveBeenCalledWith(201, { name: '於嘉丽' });
|
||||
expect(result).toEqual({
|
||||
message: '成功更新 2 名学生,失败 0 条',
|
||||
updated: [
|
||||
{ id: 201, name: '於嘉丽' },
|
||||
{ id: 172, name: '徐玚' },
|
||||
],
|
||||
failed: [],
|
||||
});
|
||||
expect(JSON.stringify(result)).not.toContain('13800138000');
|
||||
});
|
||||
|
||||
it('continues when one student cannot be updated', async () => {
|
||||
const { tool } = createTool({
|
||||
studentsService: {
|
||||
update: jest
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new NotFoundException('not found'))
|
||||
.mockResolvedValueOnce({ id: 172, name: '徐玚' }),
|
||||
},
|
||||
});
|
||||
const result = await tool.execute(validInput, {} as never);
|
||||
expect(result).toEqual({
|
||||
message: '成功更新 1 名学生,失败 1 条',
|
||||
updated: [{ id: 172, name: '徐玚' }],
|
||||
failed: [{ id: 201, error: '学生不存在' }],
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user