145 lines
4.7 KiB
TypeScript
145 lines
4.7 KiB
TypeScript
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
import { Readable } from 'node:stream';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { handleImageWorkspaceRoutes } from '@electron/api/routes/image-workspace';
|
|
import type { HostApiContext } from '@electron/api/context';
|
|
import type { ImageWorkspaceSnapshot } from '../../shared/image-workspace';
|
|
|
|
function createResponse() {
|
|
const chunks: string[] = [];
|
|
const res = {
|
|
statusCode: 0,
|
|
setHeader: vi.fn(),
|
|
end: vi.fn((chunk?: string) => {
|
|
if (chunk) chunks.push(chunk);
|
|
}),
|
|
} as unknown as ServerResponse;
|
|
return {
|
|
res,
|
|
get statusCode() {
|
|
return res.statusCode;
|
|
},
|
|
json: () => JSON.parse(chunks.join('')) as Record<string, unknown>,
|
|
};
|
|
}
|
|
|
|
function createRequest(method: string, body?: unknown): IncomingMessage {
|
|
const request = Readable.from(body === undefined ? [] : [JSON.stringify(body)]) as unknown as IncomingMessage;
|
|
request.method = method;
|
|
request.headers = body === undefined ? {} : { 'content-type': 'application/json' };
|
|
return request;
|
|
}
|
|
|
|
const localSnapshot: ImageWorkspaceSnapshot = {
|
|
capabilities: {
|
|
modes: [],
|
|
models: [],
|
|
aspectRatios: [],
|
|
resolutions: [],
|
|
outputCounts: [],
|
|
maxReferenceImages: 0,
|
|
referenceUpload: { enabled: false, acceptedMimeTypes: [] },
|
|
},
|
|
projects: [],
|
|
activeProjectId: null,
|
|
};
|
|
|
|
describe('image workspace Main route boundary', () => {
|
|
it('does not claim unrelated routes', async () => {
|
|
const response = createResponse();
|
|
const handled = await handleImageWorkspaceRoutes(
|
|
{ method: 'GET' } as IncomingMessage,
|
|
response.res,
|
|
new URL('http://127.0.0.1/api/works/projects'),
|
|
{} as never,
|
|
);
|
|
|
|
expect(handled).toBe(false);
|
|
expect(response.res.end).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('returns a stable unavailable response without fabricating cloud state', async () => {
|
|
const fetchMock = vi.fn();
|
|
vi.stubGlobal('fetch', fetchMock);
|
|
const response = createResponse();
|
|
const handled = await handleImageWorkspaceRoutes(
|
|
{ method: 'GET' } as IncomingMessage,
|
|
response.res,
|
|
new URL('http://127.0.0.1/api/works/image-workspace'),
|
|
{} as never,
|
|
);
|
|
|
|
expect(handled).toBe(true);
|
|
expect(response.statusCode).toBe(501);
|
|
expect(response.json()).toEqual({
|
|
success: false,
|
|
status: 501,
|
|
code: 'IMAGE_WORKSPACE_UNAVAILABLE',
|
|
error: '创作空间暂不可用',
|
|
});
|
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('serves the local workspace only when Main injects the development service', async () => {
|
|
const imageWorkspace = {
|
|
getSnapshot: vi.fn().mockResolvedValue(localSnapshot),
|
|
createProject: vi.fn().mockResolvedValue(localSnapshot),
|
|
addAgent: vi.fn().mockResolvedValue(localSnapshot),
|
|
sendMessage: vi.fn().mockResolvedValue(localSnapshot),
|
|
uploadReference: vi.fn(),
|
|
reset: vi.fn().mockResolvedValue(localSnapshot),
|
|
};
|
|
const ctx = { imageWorkspace } as unknown as HostApiContext;
|
|
|
|
const getResponse = createResponse();
|
|
await handleImageWorkspaceRoutes(
|
|
createRequest('GET'),
|
|
getResponse.res,
|
|
new URL('http://127.0.0.1/api/works/image-workspace'),
|
|
ctx,
|
|
);
|
|
expect(getResponse.statusCode).toBe(200);
|
|
expect(getResponse.json()).toMatchObject({ success: true, workspace: localSnapshot });
|
|
|
|
const createResponseBody = createResponse();
|
|
await handleImageWorkspaceRoutes(
|
|
createRequest('POST', { name: ' 测试项目 ' }),
|
|
createResponseBody.res,
|
|
new URL('http://127.0.0.1/api/works/image-workspace/projects'),
|
|
ctx,
|
|
);
|
|
expect(imageWorkspace.createProject).toHaveBeenCalledWith(' 测试项目 ');
|
|
|
|
const messageResponse = createResponse();
|
|
await handleImageWorkspaceRoutes(
|
|
createRequest('POST', {
|
|
projectId: 'spoofed-project',
|
|
agentId: 'agent-one',
|
|
prompt: '继续编辑',
|
|
referenceImageIds: ['reference-one'],
|
|
settings: { outputCountId: '2' },
|
|
}),
|
|
messageResponse.res,
|
|
new URL('http://127.0.0.1/api/works/image-workspace/projects/project%2Fone/messages'),
|
|
ctx,
|
|
);
|
|
expect(imageWorkspace.sendMessage).toHaveBeenCalledWith({
|
|
projectId: 'project/one',
|
|
agentId: 'agent-one',
|
|
prompt: '继续编辑',
|
|
referenceImageIds: ['reference-one'],
|
|
settings: { outputCountId: '2' },
|
|
});
|
|
|
|
const resetResponse = createResponse();
|
|
await handleImageWorkspaceRoutes(
|
|
createRequest('DELETE'),
|
|
resetResponse.res,
|
|
new URL('http://127.0.0.1/api/works/image-workspace/local-data'),
|
|
ctx,
|
|
);
|
|
expect(imageWorkspace.reset).toHaveBeenCalledOnce();
|
|
expect(resetResponse.json()).toMatchObject({ workspace: { projects: [] } });
|
|
});
|
|
});
|