feat: implement PI core chat timeline

This commit is contained in:
2026-08-24 00:34:03 +08:00
parent 54d8bb2e4a
commit 612135f911
40 changed files with 3881 additions and 119 deletions

View File

@@ -0,0 +1,45 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
const hostApi = vi.hoisted(() => ({
fetch: vi.fn(),
fetchBytes: vi.fn(),
}));
vi.mock('@/lib/host-api', () => ({
hostApiFetch: hostApi.fetch,
hostApiFetchBytes: hostApi.fetchBytes,
}));
describe('coding attachment Host facade', () => {
beforeEach(() => vi.clearAllMocks());
it('uploads raw image bytes only when invoked and returns the attachment reference', async () => {
const attachment = { attachmentId: 'attachment-1', mime: 'image/png', byteLength: 4 };
hostApi.fetch.mockResolvedValueOnce(attachment);
const { uploadCodingAttachment } = await import('@/lib/coding-attachments');
const file = new File([new Uint8Array([1, 2, 3, 4])], 'pixel.png', { type: 'image/png' });
await expect(uploadCodingAttachment(file)).resolves.toEqual(attachment);
expect(hostApi.fetch).toHaveBeenCalledOnce();
const [path, init] = hostApi.fetch.mock.calls[0] as [string, RequestInit];
expect(path).toBe('/api/coding/attachments');
expect(init).toMatchObject({ method: 'POST', headers: { 'Content-Type': 'image/png' } });
expect(Array.from(new Uint8Array(init.body as ArrayBuffer))).toEqual([1, 2, 3, 4]);
});
it('loads an authenticated binary preview without constructing a data URL', async () => {
hostApi.fetchBytes.mockResolvedValueOnce({
bytes: new Uint8Array([5, 6, 7]),
contentType: 'image/webp',
});
const { loadCodingAttachmentPreview } = await import('@/lib/coding-attachments');
await expect(loadCodingAttachmentPreview('attachment/a')).resolves.toEqual({
bytes: new Uint8Array([5, 6, 7]),
mime: 'image/webp',
});
expect(hostApi.fetchBytes).toHaveBeenCalledWith(
'/api/coding/attachments/attachment%2Fa/content',
);
});
});