

























11import fs from "node:fs";
22import os from "node:os";
33import path from "node:path";
4+import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime";
45import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
56import { prepareFileConsentActivityFs } from "./file-consent-helpers.js";
67import {
@@ -50,6 +51,7 @@ async function cleanupTempDirs(): Promise<void> {
50515152describe("msteams pending uploads (fs-backed)", () => {
5253beforeEach(() => {
54+resetPluginStateStoreForTests();
5355setMSTeamsRuntime(msteamsRuntimeStub);
5456clearPendingUploads();
5557});
@@ -105,25 +107,39 @@ describe("msteams pending uploads (fs-backed)", () => {
105107{ env },
106108);
107109108-// Confirm the backing file actually exists on disk with expected shape
110+// Confirm SQLite-backed plugin state was created instead of a new JSON store.
109111const storePath = path.join(stateDir, "msteams-pending-uploads.json");
110-const raw = await fs.promises.readFile(storePath, "utf-8");
111-const parsed = JSON.parse(raw) as {
112-version: number;
113-uploads: Record<string, { bufferBase64: string; filename: string }>;
114-};
115-expect(parsed.version).toBe(1);
116-expect(parsed.uploads["upload-x"]?.filename).toBe("secret.bin");
117-expect(Buffer.from(parsed.uploads["upload-x"].bufferBase64, "base64").toString("utf8")).toBe(
118-"top secret",
119-);
112+await expect(fs.promises.access(storePath)).rejects.toThrow();
113+await expect(
114+fs.promises.access(path.join(stateDir, "state", "openclaw.sqlite")),
115+).resolves.toBeUndefined();
120116121117// Second "process": reader using the same state dir
122118const reader = await getPendingUploadFs("upload-x", { env });
123119expect(reader?.buffer.toString("utf8")).toBe("top secret");
124120expect(reader?.filename).toBe("secret.bin");
125121});
126122123+it("stores multi-megabyte uploads by chunking payload bytes", async () => {
124+const stateDir = await makeTempStateDir();
125+const env = makeEnv(stateDir);
126+const payload = Buffer.alloc(6 * 1024 * 1024, 7);
127+128+await storePendingUploadFs(
129+{
130+id: "upload-large",
131+buffer: payload,
132+filename: "large.bin",
133+conversationId: "19:conv@thread.v2",
134+},
135+{ env },
136+);
137+138+const reader = await getPendingUploadFs("upload-large", { env });
139+expect(reader?.buffer.equals(payload)).toBe(true);
140+expect(reader?.filename).toBe("large.bin");
141+});
142+127143it("removes persisted entries", async () => {
128144const stateDir = await makeTempStateDir();
129145const env = makeEnv(stateDir);
@@ -204,14 +220,85 @@ describe("msteams pending uploads (fs-backed)", () => {
204220205221// Should not throw and should treat as empty
206222expect(await getPendingUploadFs("anything", { env })).toBeUndefined();
223+await expect(fs.promises.access(storePath)).rejects.toThrow();
224+225+const secondStateDir = await makeTempStateDir();
226+const secondEnv = makeEnv(secondStateDir);
227+const secondStorePath = path.join(secondStateDir, "msteams-pending-uploads.json");
228+await fs.promises.writeFile(
229+secondStorePath,
230+JSON.stringify({ version: 2, uploads: {} }),
231+"utf-8",
232+);
233+expect(await getPendingUploadFs("anything", { env: secondEnv })).toBeUndefined();
234+await expect(fs.promises.access(secondStorePath)).rejects.toThrow();
235+});
207236208-await fs.promises.writeFile(storePath, JSON.stringify({ version: 2, uploads: {} }), "utf-8");
209-expect(await getPendingUploadFs("anything", { env })).toBeUndefined();
237+it("imports a legacy JSON file that appears after an empty migration marker", async () => {
238+const stateDir = await makeTempStateDir();
239+const env = makeEnv(stateDir);
240+const storePath = path.join(stateDir, "msteams-pending-uploads.json");
241+242+expect(await getPendingUploadFs("upload-late", { env })).toBeUndefined();
243+await fs.promises.writeFile(
244+storePath,
245+`${JSON.stringify({
246+ version: 1,
247+ uploads: {
248+ "upload-late": {
249+ id: "upload-late",
250+ bufferBase64: Buffer.from("late payload").toString("base64"),
251+ filename: "late.txt",
252+ conversationId: "19:conv@thread.v2",
253+ createdAt: Date.now(),
254+ },
255+ },
256+ })}\n`,
257+"utf-8",
258+);
259+260+const loaded = await getPendingUploadFs("upload-late", { env });
261+expect(loaded?.filename).toBe("late.txt");
262+expect(loaded?.buffer.toString("utf8")).toBe("late payload");
263+await expect(fs.promises.access(storePath)).rejects.toThrow();
264+});
265+266+it("skips malformed legacy upload rows while importing valid rows", async () => {
267+const stateDir = await makeTempStateDir();
268+const env = makeEnv(stateDir);
269+const storePath = path.join(stateDir, "msteams-pending-uploads.json");
270+await fs.promises.writeFile(
271+storePath,
272+`${JSON.stringify({
273+ version: 1,
274+ uploads: {
275+ broken: {
276+ id: "broken",
277+ filename: "broken.txt",
278+ conversationId: "19:conv@thread.v2",
279+ createdAt: Date.now(),
280+ },
281+ valid: {
282+ id: "valid",
283+ bufferBase64: Buffer.from("valid payload").toString("base64"),
284+ filename: "valid.txt",
285+ conversationId: "19:conv@thread.v2",
286+ createdAt: Date.now(),
287+ },
288+ },
289+ })}\n`,
290+"utf-8",
291+);
292+293+expect(await getPendingUploadFs("broken", { env })).toBeUndefined();
294+const loaded = await getPendingUploadFs("valid", { env });
295+expect(loaded?.buffer.toString("utf8")).toBe("valid payload");
210296});
211297});
212298213299describe("prepareFileConsentActivityFs end-to-end", () => {
214300beforeEach(() => {
301+resetPluginStateStoreForTests();
215302setMSTeamsRuntime(msteamsRuntimeStub);
216303clearPendingUploads();
217304});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。