fix(teacher): 区分读取额度与上下文失效
This commit is contained in:
@@ -440,6 +440,58 @@ it.each(['方法老师', '朋友', '代码顾问'])('returns all three local rea
|
||||
expect(f.progress).toHaveBeenCalledWith('正在读取项目与会话…');
|
||||
});
|
||||
|
||||
it.each([false, true])('finishes six batches and rejects a seventh as a read limit (extra batch: %s)', async extraBatch => {
|
||||
const f = await fixture();
|
||||
const returned: unknown[] = [];
|
||||
const transport: TeacherCloudTransport = {
|
||||
events: vi.fn(),
|
||||
json: vi.fn(async (url, body) => {
|
||||
if (url === '/questions') return { request_id: 'question', run_id: 'run-0' };
|
||||
if (url.endsWith('/cancel')) return { status: 'cancelled' };
|
||||
if (url.endsWith('/tool-results')) {
|
||||
returned.push(body);
|
||||
return { run_id: `run-${returned.length}` };
|
||||
}
|
||||
if (!extraBatch && returned.length === 6) return { status: 'completed', output: '已根据六批资料完成检查。' };
|
||||
return { status: 'interrupted', interrupt: {
|
||||
source: 'client_read_tools', context_id: requestId,
|
||||
calls: [{ tool_call_id: `read-${returned.length}`, name: 'read_project_file', arguments: { path: 'src/game.ts' } }],
|
||||
} };
|
||||
}),
|
||||
};
|
||||
const run = prepareCloudTeacher(f.account, f.topic, requestId, f.access, f.progress, f.saveRequest, transport);
|
||||
const text = vi.fn();
|
||||
const result = run.run([{ role: 'user', content: '检查项目' }], new AbortController().signal, text);
|
||||
if (extraBatch) {
|
||||
await expect(result).rejects.toMatchObject({ code: 'teacher_read_limit', message: expect.stringContaining('读取上限') });
|
||||
expect(transport.json).toHaveBeenCalledWith('/questions/' + requestId + '/cancel', {}, expect.anything());
|
||||
} else {
|
||||
await result;
|
||||
expect(text).toHaveBeenCalledWith('已根据六批资料完成检查。');
|
||||
expect(transport.json).not.toHaveBeenCalledWith('/questions/' + requestId + '/cancel', expect.anything(), expect.anything());
|
||||
}
|
||||
expect(returned).toHaveLength(6);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ label: 'missing', calls: undefined },
|
||||
{ label: 'empty', calls: [] },
|
||||
{ label: 'oversized', calls: Array.from({ length: 33 }, () => ({})) },
|
||||
])('rejects $label read batches as protocol errors', async ({ calls }) => {
|
||||
const f = await fixture();
|
||||
const transport: TeacherCloudTransport = {
|
||||
events: vi.fn(),
|
||||
json: vi.fn(async url => {
|
||||
if (url === '/questions') return { request_id: 'question', run_id: 'one' };
|
||||
if (url.endsWith('/cancel')) return { status: 'cancelled' };
|
||||
return { status: 'interrupted', interrupt: { source: 'client_read_tools', context_id: requestId, calls } };
|
||||
}),
|
||||
};
|
||||
const run = prepareCloudTeacher(f.account, f.topic, requestId, f.access, f.progress, f.saveRequest, transport);
|
||||
await expect(run.run([], new AbortController().signal, vi.fn())).rejects.toMatchObject({ code: 'teacher_protocol_invalid' });
|
||||
expect(transport.json).not.toHaveBeenCalledWith(expect.stringContaining('/tool-results'), expect.anything(), expect.anything());
|
||||
});
|
||||
|
||||
it('rejects a stale context before reading or returning any project data', async () => {
|
||||
const f = await fixture();
|
||||
const transport: TeacherCloudTransport = {
|
||||
|
||||
Reference in New Issue
Block a user