feat(agents): 支持个人微信发布与渠道会话
This commit is contained in:
@@ -23,6 +23,8 @@ beforeEach(() => {
|
||||
api.call.mockImplementation(async (operation: string) => {
|
||||
if (operation === 'budget') return budget;
|
||||
if (operation === 'scheduleContext') return scheduleContext;
|
||||
if (operation === 'channelSelfCallers') return { items: [] };
|
||||
if (operation === 'channelBindings') return { items: [], available_accounts: [] };
|
||||
if (operation === 'threads') return { threads: [], next_offset: null };
|
||||
if (operation === 'history') return history;
|
||||
if (operation === 'attachments') return { attachments: [] };
|
||||
@@ -180,13 +182,14 @@ it('keeps publication and access controls usable when cost history is unavailabl
|
||||
api.call.mockImplementation(async (operation) => {
|
||||
if (operation === 'access') return { published_version: 1, enabled: true, share_url: 'niancode://agents/' + slug, versions: [], grants: [], applications: [] };
|
||||
if (operation === 'costs') throw new Error('费用暂不可用');
|
||||
if (operation === 'channelBindings') return { items: [], available_accounts: [] };
|
||||
if (operation === 'budget') return budget;
|
||||
throw new Error(operation);
|
||||
});
|
||||
render(<CloudAccessPanel slug={slug} revision={2} onPublished={() => undefined} />);
|
||||
expect(await screen.findByText('当前发布版本 1')).toBeVisible();
|
||||
expect(screen.getByRole('button', { name: '停用智能体' })).toBeEnabled();
|
||||
expect(await screen.findByRole('alert')).toHaveTextContent('费用暂不可用');
|
||||
expect(await screen.findByText('费用暂不可用')).toBeVisible();
|
||||
});
|
||||
|
||||
it('saves a manually created schedule disabled unless the creator selects enable', async () => {
|
||||
@@ -211,6 +214,8 @@ it('an uncertain schedule save retries the same operation and explicit enabled s
|
||||
if (operation === 'schedules') return { jobs: [] };
|
||||
if (operation === 'budget') return budget;
|
||||
if (operation === 'scheduleContext') return scheduleContext;
|
||||
if (operation === 'channelSelfCallers') return { items: [] };
|
||||
if (operation === 'channelBindings') return { items: [], available_accounts: [] };
|
||||
if (operation === 'createSchedule') { if (++attempt === 1) throw new Error('超时'); return { id: 'job' }; }
|
||||
throw new Error(operation);
|
||||
});
|
||||
@@ -230,6 +235,73 @@ it('an uncertain schedule save retries the same operation and explicit enabled s
|
||||
expect(calls[0][1].enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('sends scheduled results only to an explicitly selected own pairing and preserves that intent on retry', async () => {
|
||||
const base = api.call.getMockImplementation()!;
|
||||
let saves = 0;
|
||||
api.call.mockImplementation(async (operation, input) => {
|
||||
if (operation === 'schedules') return { jobs: [] };
|
||||
if (operation === 'channelSelfCallers') return { items: [{ caller_id: 'self-caller', channel_account_id: 'wechat-a' }] };
|
||||
if (operation === 'channelBindings') return { items: [{ id: 'wechat-a', display_name: '我的创作助手' }], available_accounts: [] };
|
||||
if (operation === 'createSchedule') { if (++saves === 1) throw new Error('连接超时'); return { id: 'job', ...input }; }
|
||||
return base(operation, input);
|
||||
});
|
||||
render(<CloudSchedules slug={slug} onOpen={() => undefined} />);
|
||||
fireEvent.click(await screen.findByRole('button', { name: '添加任务' }));
|
||||
await screen.findByRole('option', { name: '我的创作助手 · 本人对话 1' });
|
||||
expect(screen.getByLabelText('结果发送到')).toHaveValue('');
|
||||
fireEvent.change(screen.getByLabelText('任务名称'), { target: { value: '微信选题' } });
|
||||
fireEvent.change(screen.getByLabelText('目标与要求'), { target: { value: '整理三个选题' } });
|
||||
fireEvent.change(screen.getByLabelText('结果发送到'), { target: { value: 'self-caller' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存为停用任务' }));
|
||||
fireEvent.click(await screen.findByRole('button', { name: '重试保存' }));
|
||||
await screen.findByRole('button', { name: '添加任务' });
|
||||
const calls = api.call.mock.calls.filter(call => call[0] === 'createSchedule');
|
||||
expect(calls).toHaveLength(2);
|
||||
expect(calls[0][1]).toEqual(calls[1][1]);
|
||||
expect(calls[0][1].result_notification).toEqual({ enabled: true, channel_account_id: 'wechat-a', caller_id: 'self-caller' });
|
||||
expect(calls[0][1].enabled).toBe(false);
|
||||
});
|
||||
|
||||
it('pauses a schedule without dropping its own-WeChat notification target', async () => {
|
||||
const notification = { enabled: true, channel_account_id: 'wechat-a', caller_id: 'self-caller' };
|
||||
const job = { id: 'job', name: '选题', prompt: '整理选题', cron_expression: '0 9 * * *', timezone: 'Asia/Shanghai', enabled: true, result_notification: notification };
|
||||
api.call.mockImplementation(async (operation, input) => {
|
||||
if (operation === 'schedules') return { jobs: [job] };
|
||||
if (operation === 'updateSchedule') return { ...job, ...input };
|
||||
throw new Error(operation);
|
||||
});
|
||||
render(<CloudSchedules slug={slug} onOpen={() => undefined} />);
|
||||
fireEvent.click(await screen.findByRole('button', { name: '暂停' }));
|
||||
await waitFor(() => expect(api.call).toHaveBeenCalledWith('updateSchedule', expect.objectContaining({ enabled: false })));
|
||||
// Yuxi retains the notification when the key is absent; resending a revoked
|
||||
// target would revalidate it and incorrectly block the pause.
|
||||
expect(api.call.mock.calls.find(call => call[0] === 'updateSchedule')![1]).not.toHaveProperty('result_notification');
|
||||
});
|
||||
|
||||
it('keeps an unavailable existing notification target until the creator explicitly clears it', async () => {
|
||||
const base = api.call.getMockImplementation()!;
|
||||
const job = { id: 'job', name: '选题', prompt: '整理选题', cron_expression: '0 9 * * *', timezone: 'Asia/Shanghai', enabled: false,
|
||||
result_notification: { enabled: true, channel_account_id: 'old-wechat', caller_id: 'revoked-caller' } };
|
||||
api.call.mockImplementation(async (operation, input) => {
|
||||
if (operation === 'schedules') return { jobs: [job] };
|
||||
if (operation === 'updateSchedule') return { ...job, ...input };
|
||||
return base(operation, input);
|
||||
});
|
||||
render(<CloudSchedules slug={slug} onOpen={() => undefined} />);
|
||||
fireEvent.click(await screen.findByRole('button', { name: '编辑' }));
|
||||
await screen.findByRole('option', { name: '原微信对话暂不可用(保留配置)' });
|
||||
expect(screen.getByLabelText('结果发送到')).toHaveValue('revoked-caller');
|
||||
expect(api.call.mock.calls.some(call => call[0] === 'updateSchedule')).toBe(false);
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存为停用任务' }));
|
||||
await screen.findByRole('button', { name: '添加任务' });
|
||||
expect(api.call.mock.calls.find(call => call[0] === 'updateSchedule')![1]).not.toHaveProperty('result_notification');
|
||||
fireEvent.click(screen.getByRole('button', { name: '编辑' }));
|
||||
await screen.findByRole('option', { name: '原微信对话暂不可用(保留配置)' });
|
||||
fireEvent.change(screen.getByLabelText('结果发送到'), { target: { value: '' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存为停用任务' }));
|
||||
await waitFor(() => expect(api.call).toHaveBeenCalledWith('updateSchedule', expect.objectContaining({ result_notification: null })));
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('returns to a newly created conversation from history without remounting the page', async () => {
|
||||
|
||||
Reference in New Issue
Block a user