






















@@ -2,6 +2,12 @@
2233import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
44import type { ChatHost } from "./app-chat.ts";
5+import {
6+getChatAttachmentDataUrl,
7+getChatAttachmentPreviewUrl,
8+registerChatAttachmentPayload,
9+resetChatAttachmentPayloadStoreForTest,
10+} from "./chat/attachment-payload-store.ts";
511import type { GatewaySessionRow, SessionsListResult } from "./types.ts";
612713const { setLastActiveSessionKeyMock } = vi.hoisted(() => ({
@@ -18,6 +24,7 @@ let navigateChatInputHistory: typeof import("./app-chat.ts").navigateChatInputHi
1824let handleAbortChat: typeof import("./app-chat.ts").handleAbortChat;
1925let refreshChatAvatar: typeof import("./app-chat.ts").refreshChatAvatar;
2026let clearPendingQueueItemsForRun: typeof import("./app-chat.ts").clearPendingQueueItemsForRun;
27+let removeQueuedMessage: typeof import("./app-chat.ts").removeQueuedMessage;
21282229async function loadChatHelpers(): Promise<void> {
2330({
@@ -27,6 +34,7 @@ async function loadChatHelpers(): Promise<void> {
2734 handleAbortChat,
2835 refreshChatAvatar,
2936 clearPendingQueueItemsForRun,
37+ removeQueuedMessage,
3038} = await import("./app-chat.ts"));
3139}
3240@@ -117,6 +125,7 @@ describe("refreshChatAvatar", () => {
117125});
118126119127afterEach(() => {
128+resetChatAttachmentPayloadStoreForTest();
120129vi.unstubAllGlobals();
121130});
122131@@ -729,6 +738,75 @@ describe("handleSendChat", () => {
729738}),
730739]);
731740});
741+742+it("drops sent attachment payload bytes while keeping the optimistic preview URL", async () => {
743+vi.stubGlobal(
744+"URL",
745+class extends URL {
746+static createObjectURL = vi.fn(() => "blob:brief");
747+static revokeObjectURL = vi.fn();
748+},
749+);
750+const request = vi.fn(async (method: string) => {
751+if (method === "chat.send") {
752+return { status: "started", runId: "run-1" };
753+}
754+throw new Error(`Unexpected request: ${method}`);
755+});
756+const file = new File(["%PDF-1.4\n"], "brief.pdf", { type: "application/pdf" });
757+const attachment = registerChatAttachmentPayload({
758+attachment: {
759+id: "att-1",
760+mimeType: "application/pdf",
761+fileName: "brief.pdf",
762+sizeBytes: file.size,
763+},
764+dataUrl: "data:application/pdf;base64,JVBERi0xLjQK",
765+ file,
766+});
767+const host = makeHost({
768+client: { request } as unknown as ChatHost["client"],
769+chatAttachments: [attachment],
770+chatMessage: "summarize",
771+});
772+773+await handleSendChat(host);
774+775+expect(getChatAttachmentDataUrl(attachment)).toBeNull();
776+expect(getChatAttachmentPreviewUrl(attachment)).toBe("blob:brief");
777+expect(JSON.stringify(host.chatMessages)).not.toContain("JVBERi0xLjQK");
778+});
779+780+it("releases queued attachment payloads when the queued item is removed", async () => {
781+const revokeObjectURL = vi.fn();
782+vi.stubGlobal(
783+"URL",
784+class extends URL {
785+static createObjectURL = vi.fn(() => "blob:queued");
786+static revokeObjectURL = revokeObjectURL;
787+},
788+);
789+const file = new File(["%PDF-1.4\n"], "brief.pdf", { type: "application/pdf" });
790+const attachment = registerChatAttachmentPayload({
791+attachment: {
792+id: "queued-att",
793+mimeType: "application/pdf",
794+fileName: "brief.pdf",
795+sizeBytes: file.size,
796+},
797+dataUrl: "data:application/pdf;base64,JVBERi0xLjQK",
798+ file,
799+});
800+const host = makeHost({
801+chatQueue: [{ id: "queued", text: "later", createdAt: 1, attachments: [attachment] }],
802+});
803+804+removeQueuedMessage(host, "queued");
805+806+expect(host.chatQueue).toEqual([]);
807+expect(getChatAttachmentDataUrl(attachment)).toBeNull();
808+expect(revokeObjectURL).toHaveBeenCalledWith("blob:queued");
809+});
732810});
733811734812describe("handleAbortChat", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。