cc-haha/desktop/src/lib/composerAttachments.test.ts
程序员阿江(Relakkes) 6d166f43d8 fix: keep desktop attachments path-only
Desktop file attachment selection was inlining every selected file as a data URL before sending the websocket message. Large multi-file sends could inflate the renderer request body by tens of megabytes before the server had a chance to materialize uploads.

Route Tauri file selection through the native dialog so desktop sends absolute paths, while preserving browser fallback data URLs for H5. Cover both active-session and draft-session composers plus a payload-size regression case.

Constraint: Browser/H5 cannot rely on local absolute file paths, so the existing FileReader fallback remains for non-Tauri runtimes.
Rejected: Raise websocket/body limits | would keep renderer memory pressure and still send file bytes unnecessarily.
Confidence: high
Scope-risk: narrow
Directive: Do not reintroduce FileReader data URLs for Tauri desktop file-picker attachments without measuring websocket payload size.
Tested: cd desktop && bun run test src/lib/composerAttachments.test.ts src/components/chat/ChatInput.test.tsx src/pages/EmptySession.test.tsx
Tested: /tmp complex-project reproduction showed 12x3MB old inline payload at 50,333,174 bytes versus path-only payload around 1-2KB
Not-tested: Full desktop lint/build due unrelated existing Sidebar.tsx type errors in the dirty worktree
Related: https://github.com/NanmiCoder/cc-haha/issues/444
2026-05-15 10:55:35 +08:00

35 lines
1.3 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { pathToComposerAttachment } from './composerAttachments'
describe('composer attachment payloads', () => {
it('keeps many selected desktop project files as paths instead of request-body data', () => {
const projectRoot = '/tmp/cc-haha-issue-444-regression'
const files = Array.from({ length: 12 }, (_, index) => (
`${projectRoot}/assets/large-${index + 1}.bin`
))
const oldInlineAttachments = files.map((filePath) => ({
type: 'file',
name: filePath.split('/').pop(),
data: `data:application/octet-stream;base64,${'A'.repeat(256 * 1024)}`,
mimeType: 'application/octet-stream',
}))
const oldInlinePayload = JSON.stringify({
type: 'user_message',
content: 'analyze these files',
attachments: oldInlineAttachments,
})
const pathOnlyAttachments = files.map(pathToComposerAttachment)
const pathOnlyPayload = JSON.stringify({
type: 'user_message',
content: 'analyze these files',
attachments: pathOnlyAttachments,
})
expect(oldInlinePayload.length).toBeGreaterThan(3 * 1024 * 1024)
expect(pathOnlyPayload.length).toBeLessThan(3 * 1024)
expect(pathOnlyAttachments.every((attachment) => attachment.path && !attachment.data)).toBe(true)
})
})