feat: 对接设计 Agent Gateway WebSocket
需求:服务端统一 Agent Gateway 将设计任务状态流切换为 WebSocket,客户端需要实时展示生成任务并支持断线恢复。 实现:Electron Main 管理 Session、一次性 Ticket、WebSocket 心跳与游标续传,按关闭码回收会话;Renderer 继续通过本机 Host API 的 SSE 投影接收任务事件,并保留 REST 降级同步。 验证:typecheck、变更文件 ESLint、37 个聚焦测试及 build:vite 通过。
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { AppError } from '@/lib/error-model';
|
||||
import {
|
||||
createHostEventSource,
|
||||
ensureHostApiToken,
|
||||
getHostApiBase,
|
||||
hostApiFetch,
|
||||
@@ -10,17 +11,20 @@ import {
|
||||
createImageWorkspaceProject,
|
||||
fetchImageWorkspace,
|
||||
ImageWorkspaceApiError,
|
||||
openImageWorkspaceTaskEvents,
|
||||
resolveImageWorkspaceAssetUrl,
|
||||
sendImageWorkspaceMessage,
|
||||
} from '@/lib/image-workspace';
|
||||
|
||||
vi.mock('@/lib/host-api', () => ({
|
||||
hostApiFetch: vi.fn(),
|
||||
createHostEventSource: vi.fn(),
|
||||
ensureHostApiToken: vi.fn(),
|
||||
getHostApiBase: vi.fn(),
|
||||
}));
|
||||
|
||||
const hostApiFetchMock = vi.mocked(hostApiFetch);
|
||||
const createHostEventSourceMock = vi.mocked(createHostEventSource);
|
||||
const ensureHostApiTokenMock = vi.mocked(ensureHostApiToken);
|
||||
const getHostApiBaseMock = vi.mocked(getHostApiBase);
|
||||
|
||||
@@ -40,6 +44,8 @@ describe('AI design renderer API boundary', () => {
|
||||
hostApiFetchMock.mockResolvedValue({ success: true, status: 200, data: bootstrap });
|
||||
ensureHostApiTokenMock.mockReset();
|
||||
ensureHostApiTokenMock.mockResolvedValue('host-token');
|
||||
createHostEventSourceMock.mockReset();
|
||||
createHostEventSourceMock.mockReturnValue({ close: vi.fn() } as unknown as EventSource);
|
||||
getHostApiBaseMock.mockReset();
|
||||
getHostApiBaseMock.mockReturnValue('http://127.0.0.1:13210');
|
||||
});
|
||||
@@ -50,6 +56,17 @@ describe('AI design renderer API boundary', () => {
|
||||
expect(hostApiFetchMock).toHaveBeenCalledWith('/api/works/image-workspace', {});
|
||||
});
|
||||
|
||||
it('opens task events only after the local Host API token is ready', async () => {
|
||||
const source = await openImageWorkspaceTaskEvents('workspace/one');
|
||||
|
||||
expect(source).toBe(createHostEventSourceMock.mock.results[0].value);
|
||||
expect(createHostEventSourceMock).toHaveBeenCalledWith(
|
||||
'/api/works/image-workspace/workspaces/workspace%2Fone/events',
|
||||
);
|
||||
expect(ensureHostApiTokenMock.mock.invocationCallOrder[0])
|
||||
.toBeLessThan(createHostEventSourceMock.mock.invocationCallOrder[0]);
|
||||
});
|
||||
|
||||
it('creates an idempotent Workspace with a trimmed user-visible title', async () => {
|
||||
await createImageWorkspaceProject(' 角色设计 ', 'workspace-client-1');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user