Files
EasyFlow/easyflow-ui-admin/app/src/components/ai-chat/useAgentComposerDraft.test.ts
陈子默 1e6158be77 feat: 完善智能体图片聊天与会话恢复
- 增加私有图片上传、绑定、历史回显与生命周期清理

- 支持输入草稿恢复、图片交互和模型图片能力约束

- 修复旧脏会话幂等删除与前端会话恢复
2026-07-17 19:54:26 +08:00

185 lines
5.4 KiB
TypeScript

// @vitest-environment happy-dom
import { createPinia, setActivePinia } from 'pinia';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useUserStore } from '@easyflow/stores';
import {
allocateAgentComposerSession,
deleteAgentComposerDraft,
getAgentComposerDraft,
saveAgentComposerDraft,
} from './mediaApi';
import { useAgentComposerDraft } from './useAgentComposerDraft';
vi.mock('./mediaApi', () => ({
allocateAgentComposerSession: vi.fn(),
deleteAgentChatImage: vi.fn(),
deleteAgentComposerDraft: vi.fn(),
getAgentComposerDraft: vi.fn(),
loadAgentChatImage: vi.fn(),
saveAgentComposerDraft: vi.fn(),
uploadAgentChatImage: vi.fn(),
}));
describe('useAgentComposerDraft', () => {
beforeEach(() => {
setActivePinia(createPinia());
useUserStore().setUserInfo({
avatar: '',
id: 'user-1',
loginName: 'admin',
nickname: '管理员',
tenantId: 'tenant-1',
});
localStorage.clear();
vi.clearAllMocks();
vi.mocked(allocateAgentComposerSession).mockResolvedValue({
data: { mode: 'FORMAL', sessionId: '100' },
errorCode: 0,
});
vi.mocked(getAgentComposerDraft).mockResolvedValue({
data: null,
errorCode: 0,
});
vi.mocked(deleteAgentComposerDraft).mockResolvedValue({
data: undefined,
errorCode: 0,
});
vi.mocked(saveAgentComposerDraft).mockImplementation(async (draft) => ({
data: {
...draft,
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
revision: draft.revision + 1,
},
errorCode: 0,
}));
});
it('restores an unsynced local shadow and writes it back to Redis', async () => {
vi.useFakeTimers();
const first = useAgentComposerDraft('FORMAL');
await first.activate('agent-1');
first.text.value = '刷新前尚未发送的内容';
first.scheduleSave();
const restored = useAgentComposerDraft('FORMAL');
await restored.activate('agent-1');
expect(restored.sessionId.value).toBe('100');
expect(restored.text.value).toBe('刷新前尚未发送的内容');
expect(saveAgentComposerDraft).toHaveBeenCalledWith(
expect.objectContaining({
agentId: 'agent-1',
sessionId: '100',
text: '刷新前尚未发送的内容',
}),
);
vi.useRealTimers();
});
it('keeps local shadows isolated by the logged-in account', async () => {
vi.useFakeTimers();
const first = useAgentComposerDraft('FORMAL');
await first.activate('agent-1');
first.text.value = '账号一的草稿';
first.scheduleSave();
useUserStore().setUserInfo({
avatar: '',
id: 'user-2',
loginName: 'other',
nickname: '其他用户',
tenantId: 'tenant-1',
});
vi.mocked(allocateAgentComposerSession).mockResolvedValueOnce({
data: { mode: 'FORMAL', sessionId: '200' },
errorCode: 0,
});
const second = useAgentComposerDraft('FORMAL');
await second.activate('agent-1');
expect(second.sessionId.value).toBe('200');
expect(second.text.value).toBe('');
vi.useRealTimers();
});
it('serializes a debounced save and the send-time flush', async () => {
vi.useFakeTimers();
let resolveFirstSave: (() => void) | undefined;
vi.mocked(saveAgentComposerDraft).mockImplementation((draft) => {
if (!resolveFirstSave) {
return new Promise((resolve) => {
resolveFirstSave = () =>
resolve({
data: { ...draft, revision: 1 },
errorCode: 0,
});
});
}
return Promise.resolve({
data: { ...draft, revision: draft.revision + 1 },
errorCode: 0,
});
});
const composer = useAgentComposerDraft('FORMAL');
await composer.activate('agent-1');
composer.text.value = '并发保存内容';
composer.scheduleSave();
await vi.advanceTimersByTimeAsync(500);
await Promise.resolve();
const flushPromise = composer.flush();
await Promise.resolve();
expect(saveAgentComposerDraft).toHaveBeenCalledTimes(1);
resolveFirstSave?.();
await flushPromise;
expect(saveAgentComposerDraft).toHaveBeenCalledTimes(2);
expect(saveAgentComposerDraft).toHaveBeenLastCalledWith(
expect.objectContaining({ revision: 1, text: '并发保存内容' }),
);
vi.useRealTimers();
});
it('waits for an in-flight save before clearing an accepted draft', async () => {
vi.useFakeTimers();
let resolveSave: (() => void) | undefined;
vi.mocked(saveAgentComposerDraft).mockImplementation(
(draft) =>
new Promise((resolve) => {
resolveSave = () =>
resolve({
data: { ...draft, revision: 1 },
errorCode: 0,
});
}),
);
const composer = useAgentComposerDraft('DRAFT');
await composer.activate('agent-1', 'agent-draft-100');
composer.text.value = '试运行内容';
composer.scheduleSave();
await vi.advanceTimersByTimeAsync(500);
await Promise.resolve();
const accepted = composer.markAccepted();
await Promise.resolve();
expect(deleteAgentComposerDraft).not.toHaveBeenCalled();
resolveSave?.();
await accepted;
expect(deleteAgentComposerDraft).toHaveBeenCalledWith({
agentId: 'agent-1',
deleteUploads: false,
imageUploadIds: [],
mode: 'DRAFT',
sessionId: 'agent-draft-100',
});
expect(composer.text.value).toBe('');
vi.useRealTimers();
});
});